diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2019-01-02 12:33:26 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2019-03-23 18:51:22 +0100 |
commit | 597e418cb02883418f2cebb41400e8e61413f651 (patch) | |
tree | 177ad9c540b2583b6dab138c9f0490d28989c7f7 /commands | |
parent | 44f5c1c14cb1f42cc5f01739c289e9cfc83602af (diff) |
Make Page an interface
The main motivation of this commit is to add a `page.Page` interface to replace the very file-oriented `hugolib.Page` struct.
This is all a preparation step for issue #5074, "pages from other data sources".
But this also fixes a set of annoying limitations, especially related to custom output formats, and shortcodes.
Most notable changes:
* The inner content of shortcodes using the `{{%` as the outer-most delimiter will now be sent to the content renderer, e.g. Blackfriday.
This means that any markdown will partake in the global ToC and footnote context etc.
* The Custom Output formats are now "fully virtualized". This removes many of the current limitations.
* The taxonomy list type now has a reference to the `Page` object.
This improves the taxonomy template `.Title` situation and make common template constructs much simpler.
See #5074
Fixes #5763
Fixes #5758
Fixes #5090
Fixes #5204
Fixes #4695
Fixes #5607
Fixes #5707
Fixes #5719
Fixes #3113
Fixes #5706
Fixes #5767
Fixes #5723
Fixes #5769
Fixes #5770
Fixes #5771
Fixes #5759
Fixes #5776
Fixes #5777
Fixes #5778
Diffstat (limited to 'commands')
-rw-r--r-- | commands/commandeer.go | 9 | ||||
-rw-r--r-- | commands/commands.go | 20 | ||||
-rw-r--r-- | commands/commands_test.go | 21 | ||||
-rw-r--r-- | commands/convert.go | 22 | ||||
-rw-r--r-- | commands/hugo.go | 169 | ||||
-rw-r--r-- | commands/import_jekyll.go | 10 | ||||
-rw-r--r-- | commands/list.go | 14 | ||||
-rw-r--r-- | commands/new_content_test.go | 12 | ||||
-rw-r--r-- | commands/server.go | 10 |
9 files changed, 232 insertions, 55 deletions
diff --git a/commands/commandeer.go b/commands/commandeer.go index 8abb6418d..8c9da53b9 100644 --- a/commands/commandeer.go +++ b/commands/commandeer.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 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. @@ -357,6 +357,13 @@ func (c *commandeer) loadConfig(mustHaveConfigFile, running bool) error { c.changeDetector = changeDetector } + if c.Cfg.GetBool("logPathWarnings") { + fs.Destination = hugofs.NewCreateCountingFs(fs.Destination) + } + + // To debug hard-to-find path issues. + //fs.Destination = hugofs.NewStacktracerFs(fs.Destination, `fr/fr`) + err = c.initFs(fs) if err != nil { return diff --git a/commands/commands.go b/commands/commands.go index 38291fd95..fa02b2e81 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Hugo Authors. All rights reserved. +// Copyright 2019 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. @@ -23,7 +23,6 @@ import ( "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/helpers" "github.com/spf13/cobra" - "github.com/spf13/nitro" ) type commandsBuilder struct { @@ -197,6 +196,12 @@ type hugoBuilderCommon struct { gc bool + // Profile flags (for debugging of performance problems) + cpuprofile string + memprofile string + mutexprofile string + traceprofile string + // TODO(bep) var vs string logging bool verbose bool @@ -255,13 +260,22 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) { cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date and author info to the pages") cmd.Flags().BoolVar(&cc.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build") - cmd.Flags().BoolVar(&nitro.AnalysisOn, "stepAnalysis", false, "display memory and timing of different steps of the program") cmd.Flags().Bool("templateMetrics", false, "display metrics about template executions") cmd.Flags().Bool("templateMetricsHints", false, "calculate some improvement hints when combined with --templateMetrics") cmd.Flags().BoolP("forceSyncStatic", "", false, "copy all files when static is changed.") cmd.Flags().BoolP("noTimes", "", false, "don't sync modification time of files") cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files") cmd.Flags().BoolP("i18n-warnings", "", false, "print missing translations") + cmd.Flags().BoolP("path-warnings", "", false, "print warnings on duplicate target paths etc.") + cmd.Flags().StringVarP(&cc.cpuprofile, "profile-cpu", "", "", "write cpu profile to `file`") + cmd.Flags().StringVarP(&cc.memprofile, "profile-mem", "", "", "write memory profile to `file`") + cmd.Flags().StringVarP(&cc.mutexprofile, "profile-mutex", "", "", "write Mutex profile to `file`") + cmd.Flags().StringVarP(&cc.traceprofile, "trace", "", "", "write trace to `file` (not useful in general)") + + // Hide these for now. + cmd.Flags().MarkHidden("profile-cpu") + cmd.Flags().MarkHidden("profile-mem") + cmd.Flags().MarkHidden("profile-mutex") cmd.Flags().StringSlice("disableKinds", []string{}, "disable different kind of pages (home, RSS etc.)") diff --git a/commands/commands_test.go b/commands/commands_test.go index 2e8b99dc4..a1c6cdd76 100644 --- a/commands/commands_test.go +++ b/commands/commands_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 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. @@ -20,6 +20,8 @@ import ( "path/filepath" "testing" + "github.com/gohugoio/hugo/common/types" + "github.com/spf13/cobra" "github.com/spf13/viper" @@ -41,7 +43,7 @@ func TestExecute(t *testing.T) { assert.NoError(resp.Err) result := resp.Result assert.True(len(result.Sites) == 1) - assert.True(len(result.Sites[0].RegularPages) == 1) + assert.True(len(result.Sites[0].RegularPages()) == 1) } func TestCommandsPersistentFlags(t *testing.T) { @@ -75,6 +77,7 @@ func TestCommandsPersistentFlags(t *testing.T) { "--port=1366", "--renderToDisk", "--source=mysource", + "--path-warnings", }, func(commands []cmder) { var sc *serverCmd for _, command := range commands { @@ -112,6 +115,9 @@ func TestCommandsPersistentFlags(t *testing.T) { assert.True(cfg.GetBool("gc")) + // The flag is named path-warnings + assert.True(cfg.GetBool("logPathWarnings")) + // The flag is named i18n-warnings assert.True(cfg.GetBool("logI18nWarnings")) @@ -183,8 +189,8 @@ func TestCommandsExecute(t *testing.T) { } for _, test := range tests { - - hugoCmd := newCommandsBuilder().addAll().build().getCommand() + b := newCommandsBuilder().addAll().build() + hugoCmd := b.getCommand() test.flags = append(test.flags, "--quiet") hugoCmd.SetArgs(append(test.commands, test.flags...)) @@ -200,6 +206,13 @@ func TestCommandsExecute(t *testing.T) { assert.NoError(err, fmt.Sprintf("%v", test.commands)) } + // Assert that we have not left any development debug artifacts in + // the code. + if b.c != nil { + _, ok := b.c.destinationFs.(types.DevMarker) + assert.False(ok) + } + } } diff --git a/commands/convert.go b/commands/convert.go index c4f88a245..e7ba572bc 100644 --- a/commands/convert.go +++ b/commands/convert.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Hugo Authors. All rights reserved. +// Copyright 2019 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. @@ -20,6 +20,8 @@ import ( "strings" "time" + "github.com/gohugoio/hugo/resources/page" + "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/helpers" @@ -124,8 +126,8 @@ func (cc *convertCmd) convertContents(format metadecoders.Format) error { site := h.Sites[0] - site.Log.FEEDBACK.Println("processing", len(site.AllPages), "content files") - for _, p := range site.AllPages { + site.Log.FEEDBACK.Println("processing", len(site.AllPages()), "content files") + for _, p := range site.AllPages() { if err := cc.convertAndSavePage(p, site, format); err != nil { return err } @@ -133,24 +135,24 @@ func (cc *convertCmd) convertContents(format metadecoders.Format) error { return nil } -func (cc *convertCmd) convertAndSavePage(p *hugolib.Page, site *hugolib.Site, targetFormat metadecoders.Format) error { +func (cc *convertCmd) convertAndSavePage(p page.Page, site *hugolib.Site, targetFormat metadecoders.Format) error { // The resources are not in .Site.AllPages. - for _, r := range p.Resources.ByType("page") { - if err := cc.convertAndSavePage(r.(*hugolib.Page), site, targetFormat); err != nil { + for _, r := range p.Resources().ByType("page") { + if err := cc.convertAndSavePage(r.(page.Page), site, targetFormat); err != nil { return err } } - if p.Filename() == "" { + if p.File() == nil { // No content file. return nil } errMsg := fmt.Errorf("Error processing file %q", p.Path()) - site.Log.INFO.Println("Attempting to convert", p.LogicalName()) + site.Log.INFO.Println("Attempting to convert", p.File().Filename()) - f, _ := p.File.(src.ReadableFile) + f, _ := p.File().(src.ReadableFile) file, err := f.Open() if err != nil { site.Log.ERROR.Println(errMsg) @@ -186,7 +188,7 @@ func (cc *convertCmd) convertAndSavePage(p *hugolib.Page, site *hugolib.Site, ta newContent.Write(pf.content) - newFilename := p.Filename() + newFilename := p.File().Filename() if cc.outputDir != "" { contentDir := strings.TrimSuffix(newFilename, p.Path()) diff --git a/commands/hugo.go b/commands/hugo.go index 3690c0ad5..4ca0eff69 100644 --- a/commands/hugo.go +++ b/commands/hugo.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 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. @@ -18,11 +18,16 @@ package commands import ( "fmt" "io/ioutil" - "os/signal" + "runtime/pprof" + "runtime/trace" "sort" "sync/atomic" + "github.com/gohugoio/hugo/hugofs" + + "github.com/gohugoio/hugo/resources/page" + "github.com/gohugoio/hugo/common/hugo" "github.com/pkg/errors" @@ -214,6 +219,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) { "themesDir", "verbose", "verboseLog", + "duplicateTargetPaths", } // Will set a value even if it is the default. @@ -235,6 +241,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) { // Set some "config aliases" setValueFromFlag(cmd.Flags(), "destination", cfg, "publishDir", false) setValueFromFlag(cmd.Flags(), "i18n-warnings", cfg, "logI18nWarnings", false) + setValueFromFlag(cmd.Flags(), "path-warnings", cfg, "logPathWarnings", false) } @@ -290,6 +297,7 @@ func (c *commandeer) fullBuild() error { } copyStaticFunc := func() error { + cnt, err := c.copyStatic() if err != nil { if !os.IsNotExist(err) { @@ -326,7 +334,7 @@ func (c *commandeer) fullBuild() error { } for _, s := range c.hugo.Sites { - s.ProcessingStats.Static = langCount[s.Language.Lang] + s.ProcessingStats.Static = langCount[s.Language().Lang] } if c.h.gc { @@ -344,9 +352,125 @@ func (c *commandeer) fullBuild() error { } +func (c *commandeer) initCPUProfile() (func(), error) { + if c.h.cpuprofile == "" { + return nil, nil + } + + f, err := os.Create(c.h.cpuprofile) + if err != nil { + return nil, errors.Wrap(err, "failed to create CPU profile") + } + if err := pprof.StartCPUProfile(f); err != nil { + return nil, errors.Wrap(err, "failed to start CPU profile") + } + return func() { + pprof.StopCPUProfile() + f.Close() + }, nil +} + +func (c *commandeer) initMemProfile() { + if c.h.memprofile == "" { + return + } + + f, err := os.Create(c.h.memprofile) + if err != nil { + c.logger.ERROR.Println("could not create memory profile: ", err) + } + defer f.Close() + runtime.GC() // get up-to-date statistics + if err := pprof.WriteHeapProfile(f); err != nil { + c.logger.ERROR.Println("could not write memory profile: ", err) + } +} + +func (c *commandeer) initTraceProfile() (func(), error) { + if c.h.traceprofile == "" { + return nil, nil + } + + f, err := os.Create(c.h.traceprofile) + if err != nil { + return nil, errors.Wrap(err, "failed to create trace file") + } + + if err := trace.Start(f); err != nil { + return nil, errors.Wrap(err, "failed to start trace") + } + + return func() { + trace.Stop() + f.Close() + }, nil +} + +func (c *commandeer) initMutexProfile() (func(), error) { + if c.h.mutexprofile == "" { + return nil, nil + } + + f, err := os.Create(c.h.mutexprofile) + if err != nil { + return nil, err + } + + runtime.SetMutexProfileFraction(1) + + return func() { + pprof.Lookup("mutex").WriteTo(f, 0) + f.Close() + }, nil + +} + +func (c *commandeer) initProfiling() (func(), error) { + stopCPUProf, err := c.initCPUProfile() + if err != nil { + return nil, err + } + + defer c.initMemProfile() + + stopMutexProf, err := c.initMutexProfile() + if err != nil { + return nil, err + } + + stopTraceProf, err := c.initTraceProfile() + if err != nil { + return nil, err + } + + return func() { + if stopCPUProf != nil { + stopCPUProf() + } + if stopMutexProf != nil { + stopMutexProf() + } + + if stopTraceProf != nil { + stopTraceProf() + } + }, nil +} + func (c *commandeer) build() error { defer c.timeTrack(time.Now(), "Total") + stopProfiling, err := c.initProfiling() + if err != nil { + return err + } + + defer func() { + if stopProfiling != nil { + stopProfiling() + } + }() + if err := c.fullBuild(); err != nil { return err } @@ -356,6 +480,13 @@ func (c *commandeer) build() error { fmt.Println() c.hugo.PrintProcessingStats(os.Stdout) fmt.Println() + + if createCounter, ok := c.destinationFs.(hugofs.DuplicatesReporter); ok { + dupes := createCounter.ReportDuplicates() + if dupes != "" { + c.logger.WARN.Println("Duplicate target paths:", dupes) + } + } } if c.h.buildWatch { @@ -369,7 +500,7 @@ func (c *commandeer) build() error { checkErr(c.Logger, err) defer watcher.Close() - var sigs = make(chan os.Signal) + var sigs = make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) <-sigs @@ -381,6 +512,17 @@ func (c *commandeer) build() error { func (c *commandeer) serverBuild() error { defer c.timeTrack(time.Now(), "Total") + stopProfiling, err := c.initProfiling() + if err != nil { + return err + } + + defer func() { + if stopProfiling != nil { + stopProfiling() + } + }() + if err := c.fullBuild(); err != nil { return err } @@ -474,11 +616,9 @@ func (c *commandeer) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint6 } c.logger.INFO.Println("syncing static files to", publishDir) - var err error - // because we are using a baseFs (to get the union right). // set sync src to root - err = syncer.Sync(publishDir, helpers.FilePathSeparator) + err := syncer.Sync(publishDir, helpers.FilePathSeparator) if err != nil { return 0, err } @@ -619,13 +759,6 @@ func (c *commandeer) getDirList() ([]string, error) { return a, nil } -func (c *commandeer) resetAndBuildSites() (err error) { - if !c.h.quiet { - c.logger.FEEDBACK.Println("Started building sites ...") - } - return c.hugo.Build(hugolib.BuildCfg{ResetState: true}) -} - func (c *commandeer) buildSites() (err error) { return c.hugo.Build(hugolib.BuildCfg{}) } @@ -973,7 +1106,7 @@ func (c *commandeer) handleEvents(watcher *watcher.Batcher, navigate := c.Cfg.GetBool("navigateToChanged") // We have fetched the same page above, but it may have // changed. - var p *hugolib.Page + var p page.Page if navigate { if onePageName != "" { @@ -982,7 +1115,7 @@ func (c *commandeer) handleEvents(watcher *watcher.Batcher, } if p != nil { - livereload.NavigateToPathForPort(p.RelPermalink(), p.Site.ServerPort()) + livereload.NavigateToPathForPort(p.RelPermalink(), p.Site().ServerPort()) } else { livereload.ForceRefresh() } @@ -1044,9 +1177,11 @@ func (c *commandeer) isThemeVsHugoVersionMismatch(fs afero.Fs) (dir string, mism } b, err := afero.ReadFile(fs, path) + if err != nil { + continue + } tomlMeta, err := metadecoders.Default.UnmarshalToMap(b, metadecoders.TOML) - if err != nil { continue } diff --git a/commands/import_jekyll.go b/commands/import_jekyll.go index d3301b48f..1d37cfd9d 100644 --- a/commands/import_jekyll.go +++ b/commands/import_jekyll.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Hugo Authors. All rights reserved. +// Copyright 2019 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. @@ -340,7 +340,7 @@ func copyDir(source string, dest string) error { if err != nil { return err } - entries, err := ioutil.ReadDir(source) + entries, _ := ioutil.ReadDir(source) for _, entry := range entries { sfp := filepath.Join(source, entry.Name()) dfp := filepath.Join(dest, entry.Name()) @@ -373,6 +373,10 @@ func (i *importCmd) copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyllPos return err } entries, err := ioutil.ReadDir(jekyllRoot) + if err != nil { + return err + } + for _, entry := range entries { sfp := filepath.Join(jekyllRoot, entry.Name()) dfp := filepath.Join(dest, entry.Name()) @@ -464,7 +468,7 @@ func convertJekyllPost(s *hugolib.Site, path, relPath, targetDir string, draft b fs := hugofs.Os if err := helpers.WriteToDisk(targetFile, strings.NewReader(content), fs); err != nil { - return fmt.Errorf("Failed to save file %q:", filename) + return fmt.Errorf("failed to save file %q: %s", filename, err) } return nil diff --git a/commands/list.go b/commands/list.go index f49726b62..99e9afe40 100644 --- a/commands/list.go +++ b/commands/list.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Hugo Authors. All rights reserved. +// Copyright 2019 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. @@ -20,6 +20,7 @@ import ( "time" "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/resources/resource" "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" ) @@ -70,7 +71,7 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.", for _, p := range sites.Pages() { if p.IsDraft() { - jww.FEEDBACK.Println(filepath.Join(p.File.Dir(), p.File.LogicalName())) + jww.FEEDBACK.Println(filepath.Join(p.File().Dir(), p.File().LogicalName())) } } @@ -108,8 +109,8 @@ posted in the future.`, defer writer.Flush() for _, p := range sites.Pages() { - if p.IsFuture() { - err := writer.Write([]string{filepath.Join(p.File.Dir(), p.File.LogicalName()), p.PublishDate.Format(time.RFC3339)}) + if resource.IsFuture(p) { + err := writer.Write([]string{filepath.Join(p.File().Dir(), p.File().LogicalName()), p.PublishDate().Format(time.RFC3339)}) if err != nil { return newSystemError("Error writing future posts to stdout", err) } @@ -149,11 +150,12 @@ expired.`, defer writer.Flush() for _, p := range sites.Pages() { - if p.IsExpired() { - err := writer.Write([]string{filepath.Join(p.File.Dir(), p.File.LogicalName()), p.ExpiryDate.Format(time.RFC3339)}) + if resource.IsExpired(p) { + err := writer.Write([]string{filepath.Join(p.File().Dir(), p.File().LogicalName()), p.ExpiryDate().Format(time.RFC3339)}) if err != nil { return newSystemError("Error writing expired posts to stdout", err) } + } } diff --git a/commands/new_content_test.go b/commands/new_content_test.go index fb8bca7b4..5a55094d6 100644 --- a/commands/new_content_test.go +++ b/commands/new_content_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 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. @@ -62,7 +62,7 @@ func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) { _, fs := newTestCfg() n := newNewSiteCmd() - require.NoError(t, fs.Source.MkdirAll(basepath, 777)) + require.NoError(t, fs.Source.MkdirAll(basepath, 0777)) require.NoError(t, n.doNewSite(fs, basepath, false)) } @@ -72,7 +72,7 @@ func TestDoNewSite_error_base_exists(t *testing.T) { _, fs := newTestCfg() n := newNewSiteCmd() - require.NoError(t, fs.Source.MkdirAll(basepath, 777)) + require.NoError(t, fs.Source.MkdirAll(basepath, 0777)) _, err := fs.Source.Create(filepath.Join(basepath, "foo")) require.NoError(t, err) // Since the directory already exists and isn't empty, expect an error @@ -85,7 +85,7 @@ func TestDoNewSite_force_empty_dir(t *testing.T) { _, fs := newTestCfg() n := newNewSiteCmd() - require.NoError(t, fs.Source.MkdirAll(basepath, 777)) + require.NoError(t, fs.Source.MkdirAll(basepath, 0777)) require.NoError(t, n.doNewSite(fs, basepath, true)) @@ -99,7 +99,7 @@ func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) { contentPath := filepath.Join(basepath, "content") - require.NoError(t, fs.Source.MkdirAll(contentPath, 777)) + require.NoError(t, fs.Source.MkdirAll(contentPath, 0777)) require.Error(t, n.doNewSite(fs, basepath, true)) } @@ -109,7 +109,7 @@ func TestDoNewSite_error_force_config_inside_exists(t *testing.T) { n := newNewSiteCmd() configPath := filepath.Join(basepath, "config.toml") - require.NoError(t, fs.Source.MkdirAll(basepath, 777)) + require.NoError(t, fs.Source.MkdirAll(basepath, 0777)) _, err := fs.Source.Create(configPath) require.NoError(t, err) diff --git a/commands/server.go b/commands/server.go index c2bd76dae..5d50ebe2c 100644 --- a/commands/server.go +++ b/commands/server.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 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. @@ -358,7 +358,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro if err := f.c.partialReRender(p); err != nil { f.c.handleBuildErr(err, fmt.Sprintf("Failed to render %q", p)) if f.c.showErrorInBrowser { - http.Redirect(w, r, p, 301) + http.Redirect(w, r, p, http.StatusMovedPermanently) return } } @@ -386,7 +386,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro return mu, u.String(), endpoint, nil } -var logErrorRe = regexp.MustCompile("(?s)ERROR \\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2} ") +var logErrorRe = regexp.MustCompile(`(?s)ERROR \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} `) func removeErrorPrefixFromLog(content string) string { return logErrorRe.ReplaceAllLiteralString(content, "") @@ -403,7 +403,7 @@ func (c *commandeer) serve(s *serverCmd) error { if isMultiHost { for _, s := range c.hugo.Sites { baseURLs = append(baseURLs, s.BaseURL.String()) - roots = append(roots, s.Language.Lang) + roots = append(roots, s.Language().Lang) } } else { s := c.hugo.Sites[0] @@ -430,7 +430,7 @@ func (c *commandeer) serve(s *serverCmd) error { livereload.Initialize() } - var sigs = make(chan os.Signal) + var sigs = make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) for i := range baseURLs { |