Skip to content

Commit

Permalink
Also consider wrapped errors when checking for file IsNotExist errors
Browse files Browse the repository at this point in the history
Fixes #10534
  • Loading branch information
bep committed Dec 14, 2022
1 parent 87e898a commit ad20598
Show file tree
Hide file tree
Showing 21 changed files with 110 additions and 48 deletions.
9 changes: 5 additions & 4 deletions cache/filecache/filecache_pruner.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"io"
"os"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/hugofs"

"github.com/spf13/afero"
Expand All @@ -36,7 +37,7 @@ func (c Caches) Prune() (int, error) {
counter += count

if err != nil {
if os.IsNotExist(err) {
if herrors.IsNotExist(err) {
continue
}
return counter, fmt.Errorf("failed to prune cache %q: %w", k, err)
Expand Down Expand Up @@ -76,7 +77,7 @@ func (c *Cache) Prune(force bool) (int, error) {
err = c.Fs.Remove(name)
}

if err != nil && !os.IsNotExist(err) {
if err != nil && !herrors.IsNotExist(err) {
return err
}

Expand All @@ -97,7 +98,7 @@ func (c *Cache) Prune(force bool) (int, error) {
counter++
}

if err != nil && !os.IsNotExist(err) {
if err != nil && !herrors.IsNotExist(err) {
return err
}

Expand All @@ -112,7 +113,7 @@ func (c *Cache) Prune(force bool) (int, error) {
func (c *Cache) pruneRootDir(force bool) (int, error) {
info, err := c.Fs.Stat(c.pruneAllRootDir)
if err != nil {
if os.IsNotExist(err) {
if herrors.IsNotExist(err) {
return 0, nil
}
return 0, err
Expand Down
4 changes: 2 additions & 2 deletions commands/hugo.go
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ func (c *commandeer) serverBuild() error {

func (c *commandeer) copyStatic() (map[string]uint64, error) {
m, err := c.doWithPublishDirs(c.copyStaticTo)
if err == nil || os.IsNotExist(err) {
if err == nil || herrors.IsNotExist(err) {
return m, nil
}
return m, err
Expand Down Expand Up @@ -899,7 +899,7 @@ func (c *commandeer) newWatcher(pollIntervalStr string, dirList ...string) (*wat
}
unlock()
case err := <-watcher.Errors():
if err != nil && !os.IsNotExist(err) {
if err != nil && !herrors.IsNotExist(err) {
c.logger.Errorln("Error while watching:", err)
}
}
Expand Down
4 changes: 2 additions & 2 deletions commands/static_syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
package commands

import (
"os"
"path/filepath"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/hugolib/filesystems"

"github.com/fsnotify/fsnotify"
Expand Down Expand Up @@ -95,7 +95,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
// the source of that static file. In this case Hugo will incorrectly remove that file
// from the published directory.
if ev.Op&fsnotify.Rename == fsnotify.Rename || ev.Op&fsnotify.Remove == fsnotify.Remove {
if _, err := sourceFs.Fs.Stat(relPath); os.IsNotExist(err) {
if _, err := sourceFs.Fs.Stat(relPath); herrors.IsNotExist(err) {
// If file doesn't exist in any static dir, remove it
logger.Println("File no longer exists in static dir, removing", relPath)
_ = c.Fs.PublishDirStatic.RemoveAll(relPath)
Expand Down
21 changes: 19 additions & 2 deletions common/herrors/errors.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// Copyright 2022 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"io"
"os"
"runtime"
"runtime/debug"
"strconv"
Expand All @@ -38,7 +39,8 @@ type ErrorSender interface {

// Recover is a helper function that can be used to capture panics.
// Put this at the top of a method/function that crashes in a template:
// defer herrors.Recover()
//
// defer herrors.Recover()
func Recover(args ...any) {
if r := recover(); r != nil {
fmt.Println("ERR:", r)
Expand Down Expand Up @@ -69,3 +71,18 @@ func Must(err error) {
panic(err)
}
}

// IsNotExist returns true if the error is a file not found error.
// Unlike os.IsNotExist, this also considers wrapped errors.
func IsNotExist(err error) bool {
if os.IsNotExist(err) {
return true
}

// os.IsNotExist does not consider wrapped errors.
if os.IsNotExist(errors.Unwrap(err)) {
return true
}

return false
}
36 changes: 36 additions & 0 deletions common/herrors/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2022 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package herrors

import (
"fmt"
"testing"

qt "github.com/frankban/quicktest"
"github.com/spf13/afero"
)

func TestIsNotExist(t *testing.T) {
c := qt.New(t)

c.Assert(IsNotExist(afero.ErrFileNotFound), qt.Equals, true)
c.Assert(IsNotExist(afero.ErrFileExists), qt.Equals, false)
c.Assert(IsNotExist(afero.ErrDestinationExists), qt.Equals, false)
c.Assert(IsNotExist(nil), qt.Equals, false)

c.Assert(IsNotExist(fmt.Errorf("foo")), qt.Equals, false)

// os.IsNotExist returns false for wrapped errors.
c.Assert(IsNotExist(fmt.Errorf("foo: %w", afero.ErrFileNotFound)), qt.Equals, true)
}
3 changes: 2 additions & 1 deletion helpers/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"strings"
"unicode"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/text"

"github.com/gohugoio/hugo/config"
Expand Down Expand Up @@ -378,7 +379,7 @@ func OpenFileForWriting(fs afero.Fs, filename string) (afero.File, error) {
// os.Create will create any new files with mode 0666 (before umask).
f, err := fs.Create(filename)
if err != nil {
if !os.IsNotExist(err) {
if !herrors.IsNotExist(err) {
return nil, err
}
if err = fs.MkdirAll(filepath.Dir(filename), 0777); err != nil { // before umask
Expand Down
3 changes: 2 additions & 1 deletion hugofs/decorators.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"path/filepath"
"strings"

"github.com/gohugoio/hugo/common/herrors"
"github.com/spf13/afero"
)

Expand Down Expand Up @@ -224,7 +225,7 @@ func (l *baseFileDecoratorFile) Readdir(c int) (ofi []os.FileInfo, err error) {
// We need to resolve any symlink info.
fi, _, err := lstatIfPossible(l.fs.Fs, filename)
if err != nil {
if os.IsNotExist(err) {
if herrors.IsNotExist(err) {
continue
}
return nil, err
Expand Down
3 changes: 2 additions & 1 deletion hugofs/rootmapping_fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"path/filepath"
"strings"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/hugofs/files"

radix "github.com/armon/go-radix"
Expand All @@ -45,7 +46,7 @@ func NewRootMappingFs(fs afero.Fs, rms ...RootMapping) (*RootMappingFs, error) {

fi, err := fs.Stat(rm.To)
if err != nil {
if os.IsNotExist(err) {
if herrors.IsNotExist(err) {
continue
}
return nil, err
Expand Down
5 changes: 3 additions & 2 deletions hugofs/slice_fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"errors"

"github.com/gohugoio/hugo/common/herrors"
"github.com/spf13/afero"
)

Expand Down Expand Up @@ -161,7 +162,7 @@ func (fs *SliceFs) pickFirst(name string) (os.FileInfo, int, error) {
return fi, i, nil
}

if !os.IsNotExist(err) {
if !herrors.IsNotExist(err) {
// Real error
return nil, -1, err
}
Expand All @@ -175,7 +176,7 @@ func (fs *SliceFs) readDirs(name string, startIdx, count int) ([]os.FileInfo, er
collect := func(lfs *FileMeta) ([]os.FileInfo, error) {
d, err := lfs.Fs.Open(name)
if err != nil {
if !os.IsNotExist(err) {
if !herrors.IsNotExist(err) {
return nil, err
}
return nil, nil
Expand Down
5 changes: 3 additions & 2 deletions hugofs/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"sort"
"strings"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/loggers"

"errors"
Expand Down Expand Up @@ -118,7 +119,7 @@ func (w *Walkway) Walk() error {
} else {
info, _, err := lstatIfPossible(w.fs, w.root)
if err != nil {
if os.IsNotExist(err) {
if herrors.IsNotExist(err) {
return nil
}

Expand Down Expand Up @@ -154,7 +155,7 @@ func (w *Walkway) checkErr(filename string, err error) bool {
return true
}

if os.IsNotExist(err) {
if herrors.IsNotExist(err) {
// The file may be removed in process.
// This may be a ERROR situation, but it is not possible
// to determine as a general case.
Expand Down
4 changes: 2 additions & 2 deletions hugolib/codeowners.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ package hugolib

import (
"io"
"os"
"path"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/resources/page"
"github.com/hairyhenderson/go-codeowners"
Expand All @@ -32,7 +32,7 @@ func findCodeOwnersFile(dir string) (io.Reader, error) {

_, err := afs.Stat(f)
if err != nil {
if os.IsNotExist(err) {
if herrors.IsNotExist(err) {
continue
}
return nil, err
Expand Down
7 changes: 4 additions & 3 deletions hugolib/filesystems/basefs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugofs/glob"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/types"

"github.com/gohugoio/hugo/common/loggers"
Expand Down Expand Up @@ -295,15 +296,15 @@ func (s SourceFilesystems) StaticFs(lang string) afero.Fs {

// StatResource looks for a resource in these filesystems in order: static, assets and finally content.
// If found in any of them, it returns FileInfo and the relevant filesystem.
// Any non os.IsNotExist error will be returned.
// An os.IsNotExist error wil be returned only if all filesystems return such an error.
// Any non herrors.IsNotExist error will be returned.
// An herrors.IsNotExist error wil be returned only if all filesystems return such an error.
// Note that if we only wanted to find the file, we could create a composite Afero fs,
// but we also need to know which filesystem root it lives in.
func (s SourceFilesystems) StatResource(lang, filename string) (fi os.FileInfo, fs afero.Fs, err error) {
for _, fsToCheck := range []afero.Fs{s.StaticFs(lang), s.Assets.Fs, s.Content.Fs} {
fs = fsToCheck
fi, err = fs.Stat(filename)
if err == nil || !os.IsNotExist(err) {
if err == nil || !herrors.IsNotExist(err) {
return
}
}
Expand Down
3 changes: 1 addition & 2 deletions hugolib/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package hugolib
import (
"bytes"
"fmt"
"os"
"path"
"path/filepath"
"sort"
Expand Down Expand Up @@ -489,7 +488,7 @@ func (p *pageState) renderResources() (err error) {
}

if err := src.Publish(); err != nil {
if os.IsNotExist(err) {
if herrors.IsNotExist(err) {
// The resource has been deleted from the file system.
// This should be extremely rare, but can happen on live reload in server
// mode when the same resource is member of different page bundles.
Expand Down
4 changes: 2 additions & 2 deletions hugolib/pages_capture.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ package hugolib
import (
"context"
"fmt"
"os"
pth "path"
"path/filepath"
"reflect"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/maps"

"github.com/gohugoio/hugo/parser/pageparser"
Expand Down Expand Up @@ -318,7 +318,7 @@ func (c *pagesCollector) cloneFileInfo(fi hugofs.FileMetaInfo) hugofs.FileMetaIn
func (c *pagesCollector) collectDir(dirname string, partial bool, inFilter func(fim hugofs.FileMetaInfo) bool) error {
fi, err := c.fs.Stat(dirname)
if err != nil {
if os.IsNotExist(err) {
if herrors.IsNotExist(err) {
// May have been deleted.
return nil
}
Expand Down
Loading

0 comments on commit ad20598

Please sign in to comment.