hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

static_syncer.go (4391B)

    1 // Copyright 2017 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 package commands
   15 
   16 import (
   17 	"os"
   18 	"path/filepath"
   19 
   20 	"github.com/gohugoio/hugo/hugolib/filesystems"
   21 
   22 	"github.com/fsnotify/fsnotify"
   23 	"github.com/gohugoio/hugo/helpers"
   24 	"github.com/spf13/fsync"
   25 )
   26 
   27 type staticSyncer struct {
   28 	c *commandeer
   29 }
   30 
   31 func newStaticSyncer(c *commandeer) (*staticSyncer, error) {
   32 	return &staticSyncer{c: c}, nil
   33 }
   34 
   35 func (s *staticSyncer) isStatic(filename string) bool {
   36 	return s.c.hugo().BaseFs.SourceFilesystems.IsStatic(filename)
   37 }
   38 
   39 func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
   40 	c := s.c
   41 
   42 	syncFn := func(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
   43 		publishDir := helpers.FilePathSeparator
   44 
   45 		if sourceFs.PublishFolder != "" {
   46 			publishDir = filepath.Join(publishDir, sourceFs.PublishFolder)
   47 		}
   48 
   49 		syncer := fsync.NewSyncer()
   50 		syncer.NoTimes = c.Cfg.GetBool("noTimes")
   51 		syncer.NoChmod = c.Cfg.GetBool("noChmod")
   52 		syncer.ChmodFilter = chmodFilter
   53 		syncer.SrcFs = sourceFs.Fs
   54 		syncer.DestFs = c.Fs.PublishDir
   55 		if c.renderStaticToDisk {
   56 			syncer.DestFs = c.Fs.PublishDirStatic
   57 		}
   58 
   59 		// prevent spamming the log on changes
   60 		logger := helpers.NewDistinctErrorLogger()
   61 
   62 		for _, ev := range staticEvents {
   63 			// Due to our approach of layering both directories and the content's rendered output
   64 			// into one we can't accurately remove a file not in one of the source directories.
   65 			// If a file is in the local static dir and also in the theme static dir and we remove
   66 			// it from one of those locations we expect it to still exist in the destination
   67 			//
   68 			// If Hugo generates a file (from the content dir) over a static file
   69 			// the content generated file should take precedence.
   70 			//
   71 			// Because we are now watching and handling individual events it is possible that a static
   72 			// event that occupies the same path as a content generated file will take precedence
   73 			// until a regeneration of the content takes places.
   74 			//
   75 			// Hugo assumes that these cases are very rare and will permit this bad behavior
   76 			// The alternative is to track every single file and which pipeline rendered it
   77 			// and then to handle conflict resolution on every event.
   78 
   79 			fromPath := ev.Name
   80 
   81 			relPath, found := sourceFs.MakePathRelative(fromPath)
   82 
   83 			if !found {
   84 				// Not member of this virtual host.
   85 				continue
   86 			}
   87 
   88 			// Remove || rename is harder and will require an assumption.
   89 			// Hugo takes the following approach:
   90 			// If the static file exists in any of the static source directories after this event
   91 			// Hugo will re-sync it.
   92 			// If it does not exist in all of the static directories Hugo will remove it.
   93 			//
   94 			// This assumes that Hugo has not generated content on top of a static file and then removed
   95 			// the source of that static file. In this case Hugo will incorrectly remove that file
   96 			// from the published directory.
   97 			if ev.Op&fsnotify.Rename == fsnotify.Rename || ev.Op&fsnotify.Remove == fsnotify.Remove {
   98 				if _, err := sourceFs.Fs.Stat(relPath); os.IsNotExist(err) {
   99 					// If file doesn't exist in any static dir, remove it
  100 					logger.Println("File no longer exists in static dir, removing", relPath)
  101 					_ = c.Fs.PublishDirStatic.RemoveAll(relPath)
  102 
  103 				} else if err == nil {
  104 					// If file still exists, sync it
  105 					logger.Println("Syncing", relPath, "to", publishDir)
  106 
  107 					if err := syncer.Sync(relPath, relPath); err != nil {
  108 						c.logger.Errorln(err)
  109 					}
  110 				} else {
  111 					c.logger.Errorln(err)
  112 				}
  113 
  114 				continue
  115 			}
  116 
  117 			// For all other event operations Hugo will sync static.
  118 			logger.Println("Syncing", relPath, "to", publishDir)
  119 			if err := syncer.Sync(filepath.Join(publishDir, relPath), relPath); err != nil {
  120 				c.logger.Errorln(err)
  121 			}
  122 		}
  123 
  124 		return 0, nil
  125 	}
  126 
  127 	_, err := c.doWithPublishDirs(syncFn)
  128 	return err
  129 }