From 71fd79a3f44101ef515349a24527e6c0b566f368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 26 Oct 2023 09:38:13 +0200 Subject: [PATCH] Revise the deprecation logging This introduces a more automatic way of increasing the log levels for deprecation log statements based on the version it was deprecated. The thresholds are a little arbitrary, but * We log INFO for 6 releases * We log WARN for another 6 releases * THen ERROR (failing the build) This should give theme authors plenty of time to catch up without having the log filled with warnings. --- commands/commandeer.go | 19 ++++-------- common/hugo/hugo.go | 56 ++++++++++++++++++++++++++++++---- common/hugo/hugo_test.go | 15 +++++++++ common/hugo/version.go | 9 +++--- config/allconfig/allconfig.go | 3 +- helpers/general.go | 16 ---------- hugolib/hugo_sites_build.go | 7 ----- hugolib/page__meta.go | 6 ++-- hugolib/site_new.go | 14 +++------ source/fileInfo.go | 5 ++- tpl/collections/collections.go | 4 +-- tpl/lang/lang.go | 7 ++--- 12 files changed, 93 insertions(+), 68 deletions(-) diff --git a/commands/commandeer.go b/commands/commandeer.go index b4f250a32aa..c3e2019b514 100644 --- a/commands/commandeer.go +++ b/commands/commandeer.go @@ -38,6 +38,7 @@ import ( "github.com/gohugoio/hugo/common/hstrings" "github.com/gohugoio/hugo/common/htime" + "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/config" @@ -50,9 +51,7 @@ import ( "github.com/spf13/cobra" ) -var ( - errHelp = errors.New("help requested") -) +var errHelp = errors.New("help requested") // Execute executes a command. func Execute(args []string) error { @@ -182,11 +181,9 @@ func (r *rootCommand) ConfigFromConfig(key int32, oldConf *commonConfig) (*commo cfg: oldConf.cfg, fs: fs, }, nil - }) return cc, err - } func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commonConfig, error) { @@ -211,7 +208,7 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo if !cfg.IsSet("workingDir") { cfg.Set("workingDir", dir) } else { - if err := os.MkdirAll(cfg.GetString("workingDir"), 0777); err != nil { + if err := os.MkdirAll(cfg.GetString("workingDir"), 0o777); err != nil { return nil, fmt.Errorf("failed to create workingDir: %w", err) } } @@ -303,7 +300,6 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo }) return cc, err - } func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, error) { @@ -348,7 +344,6 @@ func (r *rootCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args err := b.build() return err }() - if err != nil { return err } @@ -434,12 +429,13 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) { } } else { if r.verbose { - helpers.Deprecated("--verbose", "use --logLevel info", false) + hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0") + hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0") level = logg.LevelInfo } if r.debug { - helpers.Deprecated("--debug", "use --logLevel debug", false) + hugo.Deprecate("--debug", "use --logLevel debug", "v0.114.0") level = logg.LevelDebug } } @@ -453,7 +449,6 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) { } return loggers.New(optsLogger), nil - } func (r *rootCommand) Reset() { @@ -519,7 +514,6 @@ func applyLocalFlagsBuildConfig(cmd *cobra.Command, r *rootCommand) { _ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{}) cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory") _ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"}) - } // Flags needed to do a build (used by hugo and hugo server commands) @@ -558,7 +552,6 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) { cmd.Flags().StringSlice("disableKinds", []string{}, "disable different kind of pages (home, RSS etc.)") cmd.Flags().Bool("minify", false, "minify any supported output format (HTML, XML etc.)") _ = cmd.Flags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{}) - } func (r *rootCommand) timeTrack(start time.Time, name string) { diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go index 1138cf2acb6..f6eb72d8f49 100644 --- a/common/hugo/hugo.go +++ b/common/hugo/hugo.go @@ -22,14 +22,15 @@ import ( "sort" "strings" "sync" + "time" godartsassv1 "github.com/bep/godartsass" + "github.com/bep/logg" "github.com/mitchellh/mapstructure" - "time" - "github.com/bep/godartsass/v2" "github.com/gohugoio/hugo/common/hexec" + "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/hugofs/files" "github.com/spf13/afero" @@ -183,8 +184,10 @@ type buildInfo struct { *debug.BuildInfo } -var bInfo *buildInfo -var bInfoInit sync.Once +var ( + bInfo *buildInfo + bInfoInit sync.Once +) func getBuildInfo() *buildInfo { bInfoInit.Do(func() { @@ -211,7 +214,6 @@ func getBuildInfo() *buildInfo { bInfo.GoArch = s.Value } } - }) return bInfo @@ -255,7 +257,7 @@ func GetDependencyListNonGo() []string { } if dartSass := dartSassVersion(); dartSass.ProtocolVersion != "" { - var dartSassPath = "github.com/sass/dart-sass-embedded" + dartSassPath := "github.com/sass/dart-sass-embedded" if IsDartSassV2() { dartSassPath = "github.com/sass/dart-sass" } @@ -346,3 +348,45 @@ var ( func IsDartSassV2() bool { return !strings.Contains(DartSassBinaryName, "embedded") } + +// Deprecate informs about a deprecation starting at the given version. +// +// A deprecation typically needs a simple change in the template, but doing so will make the template incompatible with older versions. +// Theme maintainers generally want +// 1. No warnings or errors in the console when building a Hugo site. +// 2. Their theme to work for at least the last few Hugo versions. +func Deprecate(item, alternative string, version string) { + level := deprecationLogLevelFromVersion(version) + DeprecateLevel(item, alternative, version, level) +} + +// DeprecateLevel informs about a deprecation logging at the given level. +func DeprecateLevel(item, alternative, version string, level logg.Level) { + var msg string + if level == logg.LevelError { + msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in Hugo %s. %s", item, version, CurrentVersion.Next().ReleaseVersion(), alternative) + } else { + msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in a future release. %s", item, version, alternative) + } + + loggers.Log().Logger().WithLevel(level).Logf(msg) +} + +// We ususally do about one minor version a month. +// We want people to run at least the current and previous version without any warnings. +// We want people who don't update Hugo that often to see the warnings and errors before we remove the feature. +func deprecationLogLevelFromVersion(ver string) logg.Level { + from := MustParseVersion(ver) + to := CurrentVersion + minorDiff := to.Minor - from.Minor + switch { + case minorDiff >= 12: + // Start failing the build after about a year. + return logg.LevelError + case minorDiff >= 6: + // Start printing warnings after about six months. + return logg.LevelWarn + default: + return logg.LevelInfo + } +} diff --git a/common/hugo/hugo_test.go b/common/hugo/hugo_test.go index 5b4a6f9ad20..e252dffbe92 100644 --- a/common/hugo/hugo_test.go +++ b/common/hugo/hugo_test.go @@ -17,6 +17,7 @@ import ( "fmt" "testing" + "github.com/bep/logg" qt "github.com/frankban/quicktest" ) @@ -49,6 +50,20 @@ func TestHugoInfo(t *testing.T) { c.Assert(devHugoInfo.IsServer(), qt.Equals, true) } +func TestDeprecationLogLevelFromVersion(t *testing.T) { + c := qt.New(t) + + c.Assert(deprecationLogLevelFromVersion("0.55.0"), qt.Equals, logg.LevelError) + ver := CurrentVersion + c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo) + ver.Minor -= 1 + c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo) + ver.Minor -= 6 + c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelWarn) + ver.Minor -= 6 + c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelError) +} + type testConfig struct { environment string running bool diff --git a/common/hugo/version.go b/common/hugo/version.go index 3bb6472e2fa..6cabfdbb964 100644 --- a/common/hugo/version.go +++ b/common/hugo/version.go @@ -67,8 +67,11 @@ func (h VersionString) String() string { // Compare implements the compare.Comparer interface. func (h VersionString) Compare(other any) int { - v := MustParseVersion(h.String()) - return compareVersions(v, other) + return compareVersions(h.Version(), other) +} + +func (h VersionString) Version() Version { + return MustParseVersion(h.String()) } // Eq implements the compare.Eqer interface. @@ -264,7 +267,6 @@ func compareFloatWithVersion(v1 float64, v2 Version) int { if v1maj > v2.Major { return 1 - } if v1maj < v2.Major { @@ -276,7 +278,6 @@ func compareFloatWithVersion(v1 float64, v2 Version) int { } return -1 - } func GoMinorVersion() int { diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index 134df1f5c25..9f0d73ecda3 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -27,6 +27,7 @@ import ( "time" "github.com/gohugoio/hugo/cache/filecache" + "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/urls" @@ -784,7 +785,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon // We accidentally allowed it in the past, so we need to support it a little longer, // But log a warning. if _, found := params[kk]; !found { - helpers.Deprecated(fmt.Sprintf("config: languages.%s.%s: custom params on the language top level", k, kk), fmt.Sprintf("Put the value below [languages.%s.params]. See https://gohugo.io/content-management/multilingual/#changes-in-hugo-01120", k), false) + hugo.Deprecate(fmt.Sprintf("config: languages.%s.%s: custom params on the language top level", k, kk), fmt.Sprintf("Put the value below [languages.%s.params]. See https://gohugo.io/content-management/multilingual/#changes-in-hugo-01120", k), "v0.112.0") params[kk] = vv } } diff --git a/helpers/general.go b/helpers/general.go index e484b92f096..281414b2a5f 100644 --- a/helpers/general.go +++ b/helpers/general.go @@ -27,9 +27,6 @@ import ( "unicode" "unicode/utf8" - "github.com/gohugoio/hugo/common/hugo" - "github.com/gohugoio/hugo/common/loggers" - "github.com/spf13/afero" "github.com/jdkato/prose/transform" @@ -246,19 +243,6 @@ func compareStringSlices(a, b []string) bool { return true } -// Deprecated informs about a deprecation, but only once for a given set of arguments' values. -// If the err flag is enabled, it logs as an ERROR (will exit with -1) and the text will -// point at the next Hugo release. -// The idea is two remove an item in two Hugo releases to give users and theme authors -// plenty of time to fix their templates. -func Deprecated(item, alternative string, err bool) { - if err { - loggers.Log().Errorf("%s is deprecated and will be removed in Hugo %s. %s", item, hugo.CurrentVersion.Next().ReleaseVersion(), alternative) - } else { - loggers.Log().Warnf("%s is deprecated and will be removed in a future release. %s", item, alternative) - } -} - // SliceToLower goes through the source slice and lowers all values. func SliceToLower(s []string) []string { if s == nil { diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 27fde5822eb..72b5fe7718c 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -25,7 +25,6 @@ import ( "github.com/bep/logg" "github.com/gohugoio/hugo/hugofs/files" - "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/publisher" "github.com/gohugoio/hugo/tpl" @@ -40,14 +39,8 @@ import ( "github.com/gohugoio/hugo/output" "github.com/fsnotify/fsnotify" - "github.com/gohugoio/hugo/helpers" ) -func init() { - // To avoid circular dependencies, we set this here. - langs.DeprecationFunc = helpers.Deprecated -} - // Build builds all sites. If filesystem events are provided, // this is considered to be a potential partial rebuild. func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error { diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go index 1c4f13d3bbe..0dd2369045f 100644 --- a/hugolib/page__meta.go +++ b/hugolib/page__meta.go @@ -130,7 +130,7 @@ func (p *pageMeta) Aliases() []string { } func (p *pageMeta) Author() page.Author { - helpers.Deprecated(".Author", "Use taxonomies.", false) + hugo.Deprecate(".Author", "Use taxonomies.", "v0.98.0") authors := p.Authors() for _, author := range authors { @@ -140,7 +140,7 @@ func (p *pageMeta) Author() page.Author { } func (p *pageMeta) Authors() page.AuthorList { - helpers.Deprecated(".Authors", "Use taxonomies.", true) + hugo.Deprecate(".Author", "Use taxonomies.", "v0.112.0") return nil } @@ -226,7 +226,7 @@ func (p *pageMeta) Path() string { {{ $path = .Path }} {{ end }} ` - helpers.Deprecated(".Path when the page is backed by a file", "We plan to use Path for a canonical source path and you probably want to check the source is a file. To get the current behaviour, you can use a construct similar to the one below:\n"+example, false) + p.s.Log.Warnln(".Path when the page is backed by a file is deprecated. We plan to use Path for a canonical source path and you probably want to check the source is a file. To get the current behaviour, you can use a construct similar to the one below:\n" + example) } diff --git a/hugolib/site_new.go b/hugolib/site_new.go index b35de79ef28..127b0e296b9 100644 --- a/hugolib/site_new.go +++ b/hugolib/site_new.go @@ -31,7 +31,6 @@ import ( "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config/allconfig" "github.com/gohugoio/hugo/deps" - "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/langs/i18n" @@ -48,9 +47,7 @@ import ( "github.com/gohugoio/hugo/tpl/tplimpl" ) -var ( - _ page.Site = (*Site)(nil) -) +var _ page.Site = (*Site)(nil) type Site struct { conf *allconfig.Config @@ -236,7 +233,6 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { } return h, err - } func newHugoSitesNew(cfg deps.DepsCfg, d *deps.Deps, sites []*Site) (*HugoSites, error) { @@ -358,7 +354,7 @@ func newHugoSitesNew(cfg deps.DepsCfg, d *deps.Deps, sites []*Site) (*HugoSites, // Returns true if we're running in a server. // Deprecated: use hugo.IsServer instead func (s *Site) IsServer() bool { - helpers.Deprecated(".Site.IsServer", "Use hugo.IsServer instead.", false) + hugo.Deprecate(".Site.IsServer", "Use hugo.IsServer instead.", "v0.120.0") return s.conf.Internal.Running } @@ -377,7 +373,7 @@ func (s *Site) Copyright() string { } func (s *Site) RSSLink() template.URL { - helpers.Deprecated("Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", false) + hugo.Deprecate("Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0") rssOutputFormat := s.home.OutputFormats().Get("rss") return template.URL(rssOutputFormat.Permalink()) } @@ -449,13 +445,13 @@ func (s *Site) Social() map[string]string { // Deprecated: Use .Site.Config.Services.Disqus.Shortname instead func (s *Site) DisqusShortname() string { - helpers.Deprecated(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", false) + hugo.Deprecate(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", "v0.120.0") return s.Config().Services.Disqus.Shortname } // Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead func (s *Site) GoogleAnalytics() string { - helpers.Deprecated(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", false) + hugo.Deprecate(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", "v0.120.0") return s.Config().Services.GoogleAnalytics.ID } diff --git a/source/fileInfo.go b/source/fileInfo.go index c58a0c3b908..60c6e6ea80b 100644 --- a/source/fileInfo.go +++ b/source/fileInfo.go @@ -21,6 +21,7 @@ import ( "time" "github.com/bep/gitmap" + "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/hugofs/files" @@ -63,7 +64,6 @@ type fileOverlap interface { } type FileWithoutOverlap interface { - // Filename gets the full path and filename to the file. Filename() string @@ -102,7 +102,6 @@ type FileWithoutOverlap interface { // FileInfo describes a source file. type FileInfo struct { - // Absolute filename to the file on disk. filename string @@ -143,7 +142,7 @@ func (fi *FileInfo) Dir() string { return fi.relDir } // Extension is an alias to Ext(). func (fi *FileInfo) Extension() string { - helpers.Deprecated(".File.Extension", "Use .File.Ext instead. ", false) + hugo.Deprecate(".File.Extension", "Use .File.Ext instead.", "v0.96.0") return fi.Ext() } diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index 2f7eb745bba..279dbb16928 100644 --- a/tpl/collections/collections.go +++ b/tpl/collections/collections.go @@ -28,10 +28,10 @@ import ( "errors" "github.com/gohugoio/hugo/common/collections" + "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/deps" - "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/tpl/compare" "github.com/spf13/cast" @@ -200,7 +200,7 @@ func (ns *Namespace) Dictionary(values ...any) (map[string]any, error) { // empty string. // Deprecated: Use the index function instead. func (ns *Namespace) EchoParam(c, k any) any { - helpers.Deprecated("collections.EchoParam", "Use the index function instead.", false) + hugo.Deprecate("collections.EchoParam", "Use the index function instead.", "v0.120.0") av, isNil := indirect(reflect.ValueOf(c)) if isNil { return "" diff --git a/tpl/lang/lang.go b/tpl/lang/lang.go index e83f70710b2..92b3aa8ff2f 100644 --- a/tpl/lang/lang.go +++ b/tpl/lang/lang.go @@ -16,19 +16,18 @@ package lang import ( "context" + "errors" "fmt" "math" "strconv" "strings" - "errors" - "github.com/gohugoio/locales" translators "github.com/gohugoio/localescompressed" "github.com/gohugoio/hugo/common/hreflect" + "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/deps" - "github.com/gohugoio/hugo/helpers" "github.com/spf13/cast" ) @@ -243,7 +242,7 @@ func (ns *Namespace) FormatNumberCustom(precision, number any, options ...any) ( // Deprecated: Use lang.FormatNumberCustom instead. func (ns *Namespace) NumFmt(precision, number any, options ...any) (string, error) { - helpers.Deprecated("lang.NumFmt", "Use lang.FormatNumberCustom instead.", false) + hugo.Deprecate("lang.NumFmt", "Use lang.FormatNumberCustom instead.", "v0.120.0") return ns.FormatNumberCustom(precision, number, options...) }