Skip to content

Commit 59af4a0

Browse files
committed
Add roles and versions as new dimensions (in addition to language)
Fixes #519
1 parent d70f828 commit 59af4a0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1783
-536
lines changed

cache/dynacache/dynacache.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ func GetOrCreatePartition[K comparable, V any](c *Cache, name string, opts Optio
340340
panic("invalid Weight, must be between 1 and 100")
341341
}
342342

343-
if partitionNameRe.FindString(name) != name {
343+
if !partitionNameRe.MatchString(name) {
344344
panic(fmt.Sprintf("invalid partition name %q", name))
345345
}
346346

common/hstrings/strings.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,3 @@ func ToString(v any) (string, bool) {
127127
}
128128
return "", false
129129
}
130-
131-
type (
132-
Strings2 [2]string
133-
Strings3 [3]string
134-
)

common/hugo/hugo.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/gohugoio/hugo/common/hexec"
3333
"github.com/gohugoio/hugo/common/loggers"
3434
"github.com/gohugoio/hugo/common/maps"
35+
"github.com/gohugoio/hugo/common/version"
3536
"github.com/gohugoio/hugo/hugofs/files"
3637

3738
"github.com/spf13/afero"
@@ -80,7 +81,7 @@ type HugoInfo struct {
8081
}
8182

8283
// Version returns the current version as a comparable version string.
83-
func (i HugoInfo) Version() VersionString {
84+
func (i HugoInfo) Version() version.VersionString {
8485
return CurrentVersion.Version()
8586
}
8687

@@ -451,7 +452,7 @@ func deprecateLevelWithLogger(item, alternative, version string, level logg.Leve
451452
// We want people to run at least the current and previous version without any warnings.
452453
// We want people who don't update Hugo that often to see the warnings and errors before we remove the feature.
453454
func deprecationLogLevelFromVersion(ver string) logg.Level {
454-
from := MustParseVersion(ver)
455+
from := version.MustParseVersion(ver)
455456
to := CurrentVersion
456457
minorDiff := to.Minor - from.Minor
457458
switch {
@@ -465,3 +466,46 @@ func deprecationLogLevelFromVersion(ver string) logg.Level {
465466
return logg.LevelInfo
466467
}
467468
}
469+
470+
// BuildVersionString creates a version string. This is what you see when
471+
// running "hugo version".
472+
func BuildVersionString() string {
473+
// program := "Hugo Static Site Generator"
474+
program := "hugo"
475+
476+
version := "v" + CurrentVersion.String()
477+
478+
bi := getBuildInfo()
479+
if bi == nil {
480+
return version
481+
}
482+
if bi.Revision != "" {
483+
version += "-" + bi.Revision
484+
}
485+
if IsExtended {
486+
version += "+extended"
487+
}
488+
if IsWithdeploy {
489+
version += "+withdeploy"
490+
}
491+
492+
osArch := bi.GoOS + "/" + bi.GoArch
493+
494+
date := bi.RevisionTime
495+
if date == "" {
496+
// Accept vendor-specified build date if .git/ is unavailable.
497+
date = buildDate
498+
}
499+
if date == "" {
500+
date = "unknown"
501+
}
502+
503+
versionString := fmt.Sprintf("%s %s %s BuildDate=%s",
504+
program, version, osArch, date)
505+
506+
if vendorInfo != "" {
507+
versionString += " VendorInfo=" + vendorInfo
508+
}
509+
510+
return versionString
511+
}

common/hugo/hugo_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
"github.com/bep/logg"
2222
qt "github.com/frankban/quicktest"
23+
"github.com/gohugoio/hugo/common/version"
2324
)
2425

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

3132
c.Assert(hugoInfo.Version(), qt.Equals, CurrentVersion.Version())
32-
c.Assert(fmt.Sprintf("%T", VersionString("")), qt.Equals, fmt.Sprintf("%T", hugoInfo.Version()))
33+
c.Assert(fmt.Sprintf("%T", version.VersionString("")), qt.Equals, fmt.Sprintf("%T", hugoInfo.Version()))
3334
c.Assert(hugoInfo.WorkingDir(), qt.Equals, "/mywork")
3435

3536
bi := getBuildInfo()

common/hugo/version_current.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2018 The Hugo Authors. All rights reserved.
1+
// Copyright 2025 The Hugo Authors. All rights reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -13,9 +13,11 @@
1313

1414
package hugo
1515

16+
import "github.com/gohugoio/hugo/common/version"
17+
1618
// CurrentVersion represents the current build version.
1719
// This should be the only one.
18-
var CurrentVersion = Version{
20+
var CurrentVersion = version.Version{
1921
Major: 0,
2022
Minor: 148,
2123
PatchLevel: 0,

common/maps/orderedintset.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2025 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 maps
15+
16+
import (
17+
"fmt"
18+
19+
"github.com/bits-and-blooms/bitset"
20+
)
21+
22+
type OrderedIntSet struct {
23+
keys []int
24+
values bitset.BitSet
25+
}
26+
27+
// NewOrderedIntSet creates a new OrderedIntSet.
28+
// Note that this is backed by https://github.com/bits-and-blooms/bitset
29+
func NewOrderedIntSet(vals ...int) *OrderedIntSet {
30+
m := &OrderedIntSet{}
31+
for _, v := range vals {
32+
m.Set(v)
33+
}
34+
return m
35+
}
36+
37+
// Set sets the value for the given key.
38+
// Note that insertion order is not affected if a key is re-inserted into the set.
39+
func (m *OrderedIntSet) Set(key int) {
40+
if m == nil {
41+
return
42+
}
43+
keyu := uint(key)
44+
if m.values.Test(keyu) {
45+
return
46+
}
47+
m.values.Set(keyu)
48+
m.keys = append(m.keys, key)
49+
}
50+
51+
func (m *OrderedIntSet) Has(key int) bool {
52+
if m == nil {
53+
return false
54+
}
55+
return m.values.Test(uint(key))
56+
}
57+
58+
func (m *OrderedIntSet) Len() int {
59+
if m == nil {
60+
return 0
61+
}
62+
return len(m.keys)
63+
}
64+
65+
func (m *OrderedIntSet) String() string {
66+
if m == nil {
67+
return "[]"
68+
}
69+
return fmt.Sprintf("%v", m.keys)
70+
}

common/maps/orderedintset_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2025 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 maps
15+
16+
import (
17+
"testing"
18+
19+
qt "github.com/frankban/quicktest"
20+
)
21+
22+
func TestOrderedIntSet(t *testing.T) {
23+
c := qt.New(t)
24+
25+
m := NewOrderedIntSet(1, 2, 3)
26+
27+
c.Assert(m.Len(), qt.Equals, 3)
28+
c.Assert(m.Has(1), qt.Equals, true)
29+
c.Assert(m.Has(4), qt.Equals, false)
30+
c.Assert(m.String(), qt.Equals, "[1 2 3]")
31+
m.Set(4)
32+
c.Assert(m.Len(), qt.Equals, 4)
33+
c.Assert(m.Has(4), qt.Equals, true)
34+
c.Assert(m.String(), qt.Equals, "[1 2 3 4]")
35+
36+
var nilset *OrderedIntSet
37+
c.Assert(nilset.Len(), qt.Equals, 0)
38+
c.Assert(nilset.Has(1), qt.Equals, false)
39+
c.Assert(nilset.String(), qt.Equals, "[]")
40+
}
41+
42+
func BenchmarkOrderedIntSetHasInSmallSet(b *testing.B) {
43+
m := NewOrderedIntSet()
44+
for i := range 8 {
45+
m.Set(i)
46+
}
47+
b.ResetTimer()
48+
for i := 0; i < b.N; i++ {
49+
m.Has(i % 32)
50+
}
51+
}
52+
53+
func BenchmarkOrderedIntSetNew(b *testing.B) {
54+
for i := 0; i < b.N; i++ {
55+
NewOrderedIntSet(1, 2, 3, 4, 5, 6, 7, 8)
56+
}
57+
}

common/paths/pathparser.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package paths
1515

1616
import (
17+
"fmt"
1718
"path"
1819
"path/filepath"
1920
"runtime"
@@ -780,3 +781,12 @@ func HasExt(p string) bool {
780781
}
781782
return false
782783
}
784+
785+
// ValidateIdentifier returns true if the given string is a valid identifier according
786+
// to Hugo's basic path normalization rules.
787+
func ValidateIdentifier(s string) error {
788+
if s == NormalizePathStringBasic(s) {
789+
return nil
790+
}
791+
return fmt.Errorf("must be all lower case and no spaces")
792+
}

common/predicate/filter.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package predicate
2+
3+
import (
4+
"iter"
5+
"strings"
6+
7+
hglob "github.com/gohugoio/hugo/hugofs/glob"
8+
)
9+
10+
type Filter[T any] struct {
11+
exclude P[T]
12+
include P[T]
13+
}
14+
15+
func (f Filter[T]) ShouldExcludeCoarse(v T) bool {
16+
return f.exclude != nil && f.exclude(v)
17+
}
18+
19+
// TODO1 simplify this.
20+
func (f Filter[T]) ShouldExcludeFine(v T) bool {
21+
if f.exclude != nil && f.exclude(v) {
22+
return true
23+
}
24+
return f.include != nil && !f.include(v)
25+
}
26+
27+
func NewFilter[T any](include, exclude P[T]) Filter[T] {
28+
return Filter[T]{exclude: exclude, include: include}
29+
}
30+
31+
func NewFilterFromGlobs(patterns []string) (Filter[string], error) {
32+
var filter Filter[string]
33+
for _, p := range patterns {
34+
p = strings.TrimSpace(p)
35+
if p == "" {
36+
continue
37+
}
38+
if strings.HasPrefix(p, "! ") {
39+
p = p[2:]
40+
g, err := hglob.GetGlob(p)
41+
if err != nil {
42+
return filter, err
43+
}
44+
fn := func(s string) bool {
45+
return g.Match(s)
46+
}
47+
filter.exclude = filter.exclude.Or(fn)
48+
} else {
49+
g, err := hglob.GetGlob(p)
50+
if err != nil {
51+
return filter, err
52+
}
53+
fn := func(s string) bool {
54+
return g.Match(s)
55+
}
56+
filter.include = filter.include.Or(fn)
57+
}
58+
}
59+
60+
return filter, nil
61+
}
62+
63+
type IndexMatcher interface {
64+
IndexMatch(filter Filter[string]) (iter.Seq[int], error)
65+
}

common/types/types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,10 @@ func NewBool(b bool) *bool {
143143
type PrintableValueProvider interface {
144144
PrintableValue() any
145145
}
146+
147+
type (
148+
Strings2 [2]string
149+
Strings3 [3]string
150+
Ints2 [2]int
151+
Ints3 [3]int
152+
)

0 commit comments

Comments
 (0)