1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-11-26 22:24:39 +01:00

Implement --force_polling

This commit is contained in:
Oliver Steele 2017-07-23 11:25:17 -04:00
parent b563633b62
commit 49a8520b76
8 changed files with 98 additions and 40 deletions

View File

@ -146,13 +146,13 @@ Muzukashii:
- [x] Directory watch
- [ ] Commands
- [x] `build`
- [x] `--source`, `--destination`, `--drafts`, `--future`, `--unpublished`, `--watch`
- [x] `--source`, `--destination`, `--drafts`, `--future`, `--unpublished`, `--watch`, `--force_polling`
- [ ] `--baseurl`, `--config`, `--incremental`, `--lsi`
- [ ] `--force_polling`, `--limit-posts`, `JEKYLL_ENV=production` not planned
- [ ] `--limit-posts`, `JEKYLL_ENV=production` not planned
- [x] `clean`
- [x] `help`
- [x] `serve`
- [x] `--open-uri`, `--host`, `--port`, `watch` (enabled by default)
- [x] `--open-uri`, `--host`, `--port`, `watch`, `--force_polling`
- [ ] `--baseurl`, `--config`, `--incremental`
- [ ] `--detach`, `--ssl`-* not planned
- [ ] `doctor`, `import`, `new`, `new-theme` not planned

View File

@ -47,7 +47,8 @@ func init() {
app.HelpFlag.Short('h')
app.Flag("profile", "Create a Go pprof CPU profile").BoolVar(&profile)
app.Flag("quiet", "Silence (some) output.").Short('q').BoolVar(&quiet)
app.Flag("verbose", "Print verbose output.").Short('V').BoolVar(&options.Verbose)
_ = app.Flag("verbose", "Print verbose output.").Short('V').Action(boolVar("verbose", &options.Verbose)).Bool()
app.Flag("force_polling", "Force watch to use polling").BoolVar(&options.ForcePolling)
build.Flag("dry-run", "Dry run").Short('n').BoolVar(&options.DryRun)
// --watch has different defaults for build and serve

View File

@ -23,7 +23,8 @@ func ParseAndRun(args []string) error {
options.Destination = &dest
}
if options.DryRun {
options.Verbose = true
verbose := true
options.Verbose = &verbose
}
return run(cmd)
}

View File

@ -36,6 +36,9 @@ type Config struct {
// Plugins
ExcerptSeparator string `yaml:"excerpt_separator"`
// Conversion
Incremental bool
// Serving
Host string
Port int
@ -44,12 +47,14 @@ type Config struct {
// Outputting
Permalink string
Timezone string
Timezone string
Verbose bool
// CLI-only
DryRun bool `yaml:"-"`
Verbose bool `yaml:"-"`
Watch bool `yaml:"-"`
DryRun bool `yaml:"-"`
ForcePolling bool `yaml:"-"`
Watch bool `yaml:"-"`
Defaults []struct {
Scope struct {

View File

@ -46,6 +46,7 @@ plugins: []
# Conversion
excerpt_separator: "\n\n"
incremental: false
# Serving
detach: false
@ -57,4 +58,6 @@ baseurl: "" # does not include hostname
permalink: date
paginate_path: /page:num
timezone: null
verbose: false
`

View File

@ -9,11 +9,11 @@ import (
type Flags struct {
// these are pointers so we can tell whether they've been set, and override
// the config file only if
Destination, Host *string
Drafts, Future, Unpublished *bool
Port *int
Destination, Host *string
Drafts, Future, Unpublished, Verbose *bool
Port *int
// these aren't in the config file, so they can be treated more conventionally
DryRun, Verbose, Watch bool
DryRun, ForcePolling, Watch bool
}
// ApplyFlags overwrites the configuration with values from flags.

View File

@ -1,7 +1,6 @@
package site
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
@ -54,7 +53,6 @@ func (s *Site) Read() error {
// Reloaded returns a new site read the same source directory, configuration file, and load flags.
func (s *Site) Reloaded(paths []string) (*Site, error) {
if s.requiresFullReload(paths) {
fmt.Println("reload everything")
copy, err := FromDirectory(s.SourceDir(), s.flags)
if err != nil {
return nil, err

View File

@ -2,12 +2,15 @@ package site
import (
"fmt"
"log"
"os"
"path/filepath"
"sync"
"time"
"github.com/fsnotify/fsnotify"
"github.com/osteele/gojekyll/utils"
"github.com/radovskyb/watcher"
)
// FilesEvent is a list of changed or added site source files, with a single
@ -23,31 +26,9 @@ func (e FilesEvent) String() string {
return fmt.Sprintf("%d file%s changed at %s", count, inflect, e.Time.Format("3:04:05PM"))
}
func (s *Site) makeEventWatcher() (<-chan string, error) {
var (
sourceDir = s.SourceDir()
filenames = make(chan string, 100)
w, err = fsnotify.NewWatcher()
)
if err != nil {
return nil, err
}
go func() {
for {
select {
case event := <-w.Events:
filenames <- utils.MustRel(sourceDir, event.Name)
case err := <-w.Errors:
fmt.Fprintln(os.Stderr, "error:", err)
}
}
}()
return filenames, w.Add(sourceDir)
}
// WatchFiles sends FilesEvent on changes within the site directory.
func (s *Site) WatchFiles() (<-chan FilesEvent, error) {
filenames, err := s.makeEventWatcher()
filenames, err := s.makeFileWatcher()
if err != nil {
return nil, err
}
@ -59,7 +40,7 @@ func (s *Site) WatchFiles() (<-chan FilesEvent, error) {
for {
paths := s.sitePaths(<-debounced)
if len(paths) > 0 {
// Make up a new timestamp. Except under pathological
// Create a new timestamp. Except under pathological
// circumstances, it will be close enough.
filesets <- FilesEvent{time.Now(), paths}
}
@ -105,6 +86,72 @@ func (s *Site) WatchRebuild() (<-chan interface{}, error) {
return events, nil
}
func (s *Site) makePollingWatcher() (<-chan string, error) {
var (
sourceDir = utils.MustAbs(s.SourceDir())
filenames = make(chan string, 100)
w = watcher.New()
)
if err := w.AddRecursive(sourceDir); err != nil {
return nil, err
}
for _, path := range s.config.Exclude {
if err := w.Ignore(filepath.Join(sourceDir, path)); err != nil {
return nil, err
}
}
if err := w.Ignore(s.DestDir()); err != nil {
return nil, err
}
go func() {
for {
select {
case event := <-w.Event:
filenames <- utils.MustRel(sourceDir, event.Path)
case err := <-w.Error:
fmt.Fprintln(os.Stderr, "error:", err)
case <-w.Closed:
return
}
}
}()
go func() {
if err := w.Start(time.Millisecond * 250); err != nil {
log.Fatal(err)
}
}()
return filenames, nil
}
func (s *Site) makeEventWatcher() (<-chan string, error) {
var (
sourceDir = s.SourceDir()
filenames = make(chan string, 100)
w, err = fsnotify.NewWatcher()
)
if err != nil {
return nil, err
}
go func() {
for {
select {
case event := <-w.Events:
filenames <- utils.MustRel(sourceDir, event.Name)
case err := <-w.Errors:
fmt.Fprintln(os.Stderr, "error:", err)
}
}
}()
return filenames, w.Add(sourceDir)
}
func (s *Site) makeFileWatcher() (<-chan string, error) {
if s.config.ForcePolling {
return s.makePollingWatcher()
}
return s.makeEventWatcher()
}
// reloads and rebuilds the site; returns a copy and count
func (s *Site) rebuild(paths []string) (r *Site, n int, err error) {
r, err = s.Reloaded(paths)
@ -136,15 +183,18 @@ func (s *Site) sitePaths(filenames []string) []string {
// faster than interval
// TODO consider https://github.com/ReactiveX/RxGo
func debounce(interval time.Duration, input <-chan string) <-chan []string {
output := make(chan []string)
var (
pending = []string{}
output = make(chan []string)
ticker <-chan time.Time
)
go func() {
for {
select {
case value := <-input:
if value == "." {
continue
}
pending = append(pending, value)
ticker = time.After(interval) // replaces the previous ticker
case <-ticker: