Skip to content

Add roles and versions as new dimensions (in addition to language) #13679

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cache/dynacache/dynacache.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ func GetOrCreatePartition[K comparable, V any](c *Cache, name string, opts Optio
panic("invalid Weight, must be between 1 and 100")
}

if partitionNameRe.FindString(name) != name {
if !partitionNameRe.MatchString(name) {
panic(fmt.Sprintf("invalid partition name %q", name))
}

Expand Down
3 changes: 2 additions & 1 deletion commands/hugobuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/bep/simplecobra"
"github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hstrings"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
Expand Down Expand Up @@ -142,7 +143,7 @@ func (c *hugoBuilder) getDirList() ([]string, error) {
return nil, err
}

return helpers.UniqueStringsSorted(h.PathSpec.BaseFs.WatchFilenames()), nil
return hstrings.UniqueStringsSorted(h.PathSpec.BaseFs.WatchFilenames()), nil
}

func (c *hugoBuilder) initCPUProfile() (func(), error) {
Expand Down
3 changes: 2 additions & 1 deletion commands/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
"github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/tpl/tplimpl"

"github.com/gohugoio/hugo/common/types"
Expand Down Expand Up @@ -879,7 +880,7 @@ func (c *serverCommand) serve() error {
if isMultihost {
for _, l := range conf.configs.ConfigLangs() {
baseURLs = append(baseURLs, l.BaseURL())
roots = append(roots, l.Language().Lang)
roots = append(roots, l.Language().(*langs.Language).Lang)
}
} else {
l := conf.configs.GetFirstLanguageConfig()
Expand Down
43 changes: 43 additions & 0 deletions common/hdebug/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2025 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 hdebug

import (
"fmt"
"strings"

"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/htesting"
)

// Printf is a debug print function that should be removed before committing code to the repository.
func Printf(format string, args ...any) {
if htesting.IsRealCI() {
panic("hdebug.Print statements should be removed before committing code!")
}
if len(args) == 1 && !strings.Contains(format, "%") {
format = format + ": %v"
}
format = format + "\n"
fmt.Printf(format, args...)
}

func AssertNotNil(a any) {
if htesting.IsRealCI() {
panic("hdebug.AssertNotNil statements should be removed before committing code!")
}
if types.IsNil(a) {
panic("hdebug.AssertNotNil: value is nil")
}
}
7 changes: 5 additions & 2 deletions common/herrors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"os"
"regexp"
"runtime"
"runtime/debug"
"strings"
"time"
)
Expand All @@ -42,10 +41,14 @@ type ErrorSender interface {
// Put this at the top of a method/function that crashes in a template:
//
// defer herrors.Recover()
//
// TODO1 remove usage.
func Recover(args ...any) {
if r := recover(); r != nil {
fmt.Println("ERR:", r)
args = append(args, "stacktrace from panic: \n"+string(debug.Stack()), "\n")
buf := make([]byte, 64<<10)
buf = buf[:runtime.Stack(buf, false)]
args = append(args, "stacktrace from panic: \n"+string(buf), "\n")
fmt.Println(args...)
}
}
Expand Down
63 changes: 59 additions & 4 deletions common/hstrings/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"fmt"
"regexp"
"slices"
"sort"
"strings"
"sync"

Expand Down Expand Up @@ -128,7 +129,61 @@ func ToString(v any) (string, bool) {
return "", false
}

type (
Strings2 [2]string
Strings3 [3]string
)
// UniqueStrings returns a new slice with any duplicates removed.
func UniqueStrings(s []string) []string {
unique := make([]string, 0, len(s))
for i, val := range s {
var seen bool
for j := range i {
if s[j] == val {
seen = true
break
}
}
if !seen {
unique = append(unique, val)
}
}
return unique
}

// UniqueStringsReuse returns a slice with any duplicates removed.
// It will modify the input slice.
func UniqueStringsReuse(s []string) []string {
result := s[:0]
for i, val := range s {
var seen bool

for j := range i {
if s[j] == val {
seen = true
break
}
}

if !seen {
result = append(result, val)
}
}
return result
}

// UniqueStringsSorted returns a sorted slice with any duplicates removed.
// It will modify the input slice.
func UniqueStringsSorted(s []string) []string {
if len(s) == 0 {
return nil
}
ss := sort.StringSlice(s)
ss.Sort()
i := 0
for j := 1; j < len(s); j++ {
if !ss.Less(i, j) {
continue
}
i++
s[i] = s[j]
}

return s[:i+1]
}
81 changes: 80 additions & 1 deletion common/hstrings/strings_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
// Copyright 2025 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 @@ -14,6 +14,7 @@
package hstrings

import (
"reflect"
"regexp"
"testing"

Expand Down Expand Up @@ -43,6 +44,84 @@ func TestGetOrCompileRegexp(t *testing.T) {
c.Assert(re.MatchString("123"), qt.Equals, true)
}

func TestUniqueStrings(t *testing.T) {
in := []string{"a", "b", "a", "b", "c", "", "a", "", "d"}
output := UniqueStrings(in)
expected := []string{"a", "b", "c", "", "d"}
if !reflect.DeepEqual(output, expected) {
t.Errorf("Expected %#v, got %#v\n", expected, output)
}
}

func TestUniqueStringsReuse(t *testing.T) {
in := []string{"a", "b", "a", "b", "c", "", "a", "", "d"}
output := UniqueStringsReuse(in)
expected := []string{"a", "b", "c", "", "d"}
if !reflect.DeepEqual(output, expected) {
t.Errorf("Expected %#v, got %#v\n", expected, output)
}
}

func TestUniqueStringsSorted(t *testing.T) {
c := qt.New(t)
in := []string{"a", "a", "b", "c", "b", "", "a", "", "d"}
output := UniqueStringsSorted(in)
expected := []string{"", "a", "b", "c", "d"}
c.Assert(output, qt.DeepEquals, expected)
c.Assert(UniqueStringsSorted(nil), qt.IsNil)
}

func BenchmarkUniqueStrings(b *testing.B) {
input := []string{"a", "b", "d", "e", "d", "h", "a", "i"}

b.Run("Safe", func(b *testing.B) {
for i := 0; i < b.N; i++ {
result := UniqueStrings(input)
if len(result) != 6 {
b.Fatalf("invalid count: %d", len(result))
}
}
})

b.Run("Reuse slice", func(b *testing.B) {
b.StopTimer()
inputs := make([][]string, b.N)
for i := 0; i < b.N; i++ {
inputc := make([]string, len(input))
copy(inputc, input)
inputs[i] = inputc
}
b.StartTimer()
for i := 0; i < b.N; i++ {
inputc := inputs[i]

result := UniqueStringsReuse(inputc)
if len(result) != 6 {
b.Fatalf("invalid count: %d", len(result))
}
}
})

b.Run("Reuse slice sorted", func(b *testing.B) {
b.StopTimer()
inputs := make([][]string, b.N)
for i := 0; i < b.N; i++ {
inputc := make([]string, len(input))
copy(inputc, input)
inputs[i] = inputc
}
b.StartTimer()
for i := 0; i < b.N; i++ {
inputc := inputs[i]

result := UniqueStringsSorted(inputc)
if len(result) != 6 {
b.Fatalf("invalid count: %d", len(result))
}
}
})
}

func BenchmarkGetOrCompileRegexp(b *testing.B) {
for i := 0; i < b.N; i++ {
GetOrCompileRegexp(`\d+`)
Expand Down
48 changes: 46 additions & 2 deletions common/hugo/hugo.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/version"
"github.com/gohugoio/hugo/hugofs/files"

"github.com/spf13/afero"
Expand Down Expand Up @@ -80,7 +81,7 @@ type HugoInfo struct {
}

// Version returns the current version as a comparable version string.
func (i HugoInfo) Version() VersionString {
func (i HugoInfo) Version() version.VersionString {
return CurrentVersion.Version()
}

Expand Down Expand Up @@ -451,7 +452,7 @@ func deprecateLevelWithLogger(item, alternative, version string, level logg.Leve
// 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)
from := version.MustParseVersion(ver)
to := CurrentVersion
minorDiff := to.Minor - from.Minor
switch {
Expand All @@ -465,3 +466,46 @@ func deprecationLogLevelFromVersion(ver string) logg.Level {
return logg.LevelInfo
}
}

// BuildVersionString creates a version string. This is what you see when
// running "hugo version".
func BuildVersionString() string {
// program := "Hugo Static Site Generator"
program := "hugo"

version := "v" + CurrentVersion.String()

bi := getBuildInfo()
if bi == nil {
return version
}
if bi.Revision != "" {
version += "-" + bi.Revision
}
if IsExtended {
version += "+extended"
}
if IsWithdeploy {
version += "+withdeploy"
}

osArch := bi.GoOS + "/" + bi.GoArch

date := bi.RevisionTime
if date == "" {
// Accept vendor-specified build date if .git/ is unavailable.
date = buildDate
}
if date == "" {
date = "unknown"
}

versionString := fmt.Sprintf("%s %s %s BuildDate=%s",
program, version, osArch, date)

if vendorInfo != "" {
versionString += " VendorInfo=" + vendorInfo
}

return versionString
}
3 changes: 2 additions & 1 deletion common/hugo/hugo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/version"
)

func TestHugoInfo(t *testing.T) {
Expand All @@ -29,7 +30,7 @@ func TestHugoInfo(t *testing.T) {
hugoInfo := NewInfo(conf, nil)

c.Assert(hugoInfo.Version(), qt.Equals, CurrentVersion.Version())
c.Assert(fmt.Sprintf("%T", VersionString("")), qt.Equals, fmt.Sprintf("%T", hugoInfo.Version()))
c.Assert(fmt.Sprintf("%T", version.VersionString("")), qt.Equals, fmt.Sprintf("%T", hugoInfo.Version()))
c.Assert(hugoInfo.WorkingDir(), qt.Equals, "/mywork")

bi := getBuildInfo()
Expand Down
6 changes: 4 additions & 2 deletions common/hugo/version_current.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// Copyright 2025 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 @@ -13,9 +13,11 @@

package hugo

import "github.com/gohugoio/hugo/common/version"

// CurrentVersion represents the current build version.
// This should be the only one.
var CurrentVersion = Version{
var CurrentVersion = version.Version{
Major: 0,
Minor: 148,
PatchLevel: 0,
Expand Down
Loading