mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-26 16:28:52 +00:00
Compare commits
208 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35c841e4b3 | |||
| 4dcf38968f | |||
| b1d207700d | |||
| 4914b7f18c | |||
| 9e06fd3394 | |||
| 9c2b2414d2 | |||
| ab21433689 | |||
| 23fcfb7f74 | |||
| 47bf2fcbe2 | |||
| 16fb2cae88 | |||
| 2bedcf3d18 | |||
| 935999e2fa | |||
| 20c3bae2c4 | |||
| 29b6e133a8 | |||
| 7f8ab74681 | |||
| 59bcc098cf | |||
| e26ba752d2 | |||
| bcf07fa639 | |||
| b6a7568131 | |||
| acf01bfb78 | |||
| 27b22cd87e | |||
| a2488b1c95 | |||
| 8f60c0c1ec | |||
| 9dc608084b | |||
| a349aafb7f | |||
| b8fbd4a578 | |||
| e2b2092ce6 | |||
| f4df7b88b3 | |||
| 3d9bd404ee | |||
| 5f5e55aa8d | |||
| 0baa4f9837 | |||
| aaaf1c8df5 | |||
| d3d4ab41a9 | |||
| 28d844642a | |||
| eb5fd3127f | |||
| 3ed28e4bfe | |||
| db14238ba3 | |||
| 3f64b5a3de | |||
| 66904097e0 | |||
| e54139c85b | |||
| 3710a5ec7e | |||
| 705e3cd5f5 | |||
| 8c61fd2541 | |||
| d3145e4e5d | |||
| 743a1da3ef | |||
| 123901b741 | |||
| 71fd79a3f4 | |||
| c4a530f104 | |||
| 272484f8bf | |||
| de4e466036 | |||
| c23a0c4a0f | |||
| a95670b981 | |||
| 46bdc03885 | |||
| 5160c7efa5 | |||
| e2dd4cd05f | |||
| e509cac533 | |||
| fd38171810 | |||
| 2eca1b3cc1 | |||
| 625162674d | |||
| a692278bc6 | |||
| d4016dd5cd | |||
| 4910312ee7 | |||
| 1b5f78b6b7 | |||
| d5d0f420d8 | |||
| 5993afa4c5 | |||
| d1b4458536 | |||
| 274852bcf2 | |||
| 37a2d5eb4e | |||
| 4c95389c25 | |||
| 3af8bded26 | |||
| 46da0b7aaa | |||
| 75f56b4ce6 | |||
| d234a963eb | |||
| b84644c008 | |||
| a9d19dbdd6 | |||
| 12d713176f | |||
| 6b65b2fae6 | |||
| 1768684d8b | |||
| 9aec42c545 | |||
| eb0ed33dfd | |||
| 6a246d1152 | |||
| ef0e7149d6 | |||
| c32094ace1 | |||
| a262fd4ddb | |||
| f0d32455d2 | |||
| e8bc8e6d02 | |||
| f9b3c0f486 | |||
| 11fcda971c | |||
| f31375d4cc | |||
| 6415b599be | |||
| 1e9b87f760 | |||
| 275c0acbf6 | |||
| f9163155d3 | |||
| 79a17d9e55 | |||
| 75c0f88289 | |||
| 18ce854626 | |||
| 69f5bad40f | |||
| 525bed9919 | |||
| 2ae4786ca1 | |||
| da7983ac4b | |||
| df5d76fed9 | |||
| 7362ba2204 | |||
| 0eb480aa1d | |||
| 7e9092eeeb | |||
| 0200eaf58f | |||
| 28b640a220 | |||
| db45dbbee8 | |||
| 77b976dd92 | |||
| e847a98db6 | |||
| 45c9bbc6ca | |||
| 94fbab2a88 | |||
| c3f273b2d7 | |||
| d7dcc76d27 | |||
| 9bf76fd7e8 | |||
| 15d3e48cec | |||
| b2a02c3c5a | |||
| 6821d6f159 | |||
| 93c7ad12ec | |||
| d2ae9e1363 | |||
| 3a8aad6b19 | |||
| a7b93e6564 | |||
| 65871d5cf4 | |||
| 2e4bf89ec7 | |||
| ebaa733d45 | |||
| 24b1be45c1 | |||
| 111f02db2a | |||
| 8a08f91d5b | |||
| dcf425c846 | |||
| 9a8c84d600 | |||
| a19d03b0e6 | |||
| cdf0b3b7a6 | |||
| d979831dbd | |||
| bcf7421ea5 | |||
| b6538532f4 | |||
| 90944aa261 | |||
| db7bc49690 | |||
| a2f6400d65 | |||
| 7d74cd0cc8 | |||
| d139f30234 | |||
| b1b6912412 | |||
| 0de81c6430 | |||
| 851bf3515e | |||
| d4a6c16c17 | |||
| 5d5fb22ead | |||
| 7c62d6ef16 | |||
| 641390f8f5 | |||
| 2e6191b2e0 | |||
| 22861cb4dc | |||
| 16da1ade70 | |||
| a3d42a277d | |||
| 2c20fd557a | |||
| 243736e751 | |||
| bf891c2259 | |||
| da0df0ada3 | |||
| 0885f8ec2b | |||
| 61be050a95 | |||
| 65af75fb00 | |||
| 2d75f74b86 | |||
| 2ac3d61604 | |||
| ade7ec8187 | |||
| 8fa8ce3e48 | |||
| 9dce45c25d | |||
| 239f2e2c99 | |||
| 3e1ea030a5 | |||
| 30885a6c5f | |||
| 58da8554cb | |||
| 92c1594372 | |||
| 5a7e0da84e | |||
| d7db096a9d | |||
| c1df5b1b0b | |||
| be8e2de597 | |||
| d297c8e1b5 | |||
| fbb8eb39ec | |||
| 87d9bffe74 | |||
| d9fdcbe939 | |||
| 295d733883 | |||
| d5247788ee | |||
| 036e260d8d | |||
| a50356b9a2 | |||
| 8859be1c01 | |||
| 87de22d746 | |||
| bec9b80d95 | |||
| 1c97095ac0 | |||
| b3cb6788b2 | |||
| 5542f02fbc | |||
| 0bc7ed9a17 | |||
| 36b5126050 | |||
| 2589b1295c | |||
| ef6e813ca8 | |||
| f4598a0986 | |||
| dc2a544fac | |||
| 916397320b | |||
| b3f10556f0 | |||
| 4d7af757c9 | |||
| 7f058b8bab | |||
| 575d7f8068 | |||
| 739d10e8bd | |||
| 0dbe0f1a07 | |||
| dc95245212 | |||
| d70b6c7d01 | |||
| d947db3713 | |||
| d8c94c354a | |||
| 6bbec90014 | |||
| 5bd22ba85f | |||
| 7ae62f4aa3 | |||
| f1a061e9ed | |||
| 2f11e673c5 | |||
| 387c5f60f9 |
@@ -4,7 +4,7 @@ parameters:
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.22000.20100
|
||||
- image: bepsays/ci-hugoreleaser:1.22100.20100
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22000.20100
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22100.20100
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
|
||||
@@ -5,7 +5,12 @@ assignees: ''
|
||||
about: Create a report to help us improve
|
||||
---
|
||||
|
||||
|
||||
<!--
|
||||
Please do not use the issue queue for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the forum:
|
||||
|
||||
https://discourse.gohugo.io
|
||||
-->
|
||||
|
||||
<!-- Please answer these questions before submitting your issue. Thanks! -->
|
||||
|
||||
### What version of Hugo are you using (`hugo version`)?
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.20.x]
|
||||
go-version: [1.21.x]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.19.x,1.20.x]
|
||||
go-version: [1.20.x,1.21.x]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@ee2113536afb7f793eed4ce60e8d3b26db912da4
|
||||
with:
|
||||
ruby-version: '2.7'
|
||||
ruby-version: '2.7'
|
||||
bundler-cache: true #
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@3105fb18c05ddd93efea5f9e0bef7a03a6e9e7df
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
run: |
|
||||
Choco-Install -PackageName mingw -ArgumentList "--version","12.2.0","--allow-downgrade"
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install dart-sass-embedded Linux
|
||||
name: Install dart-sass Linux
|
||||
run: |
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
echo "$GOBIN"
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'macos-latest'
|
||||
name: Install dart-sass-embedded MacOS
|
||||
name: Install dart-sass MacOS
|
||||
run: |
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
tar -xvf "dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'windows-latest'
|
||||
name: Install dart-sass-embedded Windows
|
||||
name: Install dart-sass Windows
|
||||
run: |
|
||||
echo "Install Dart Sass version ${env:SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${env:SASS_VERSION}/dart-sass-${env:SASS_VERSION}-windows-x64.zip";
|
||||
|
||||
@@ -122,8 +122,6 @@ cd hugo
|
||||
go install
|
||||
```
|
||||
|
||||
>Note: Some Go tools may not be fully updated to support Go Modules yet. One example would be LiteIDE. Follow [this workaround](https://github.com/visualfc/liteide/issues/986#issuecomment-428117702) for how to continue to work with Hugo below `GOPATH`.
|
||||
|
||||
For some convenient build and test targets, you also will want to install Mage:
|
||||
|
||||
```bash
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
# Twitter: https://twitter.com/gohugoio
|
||||
# Website: https://gohugo.io/
|
||||
|
||||
FROM golang:1.19-alpine AS build
|
||||
FROM golang:1.21-alpine AS build
|
||||
|
||||
# Optionally set HUGO_BUILD_TAGS to "extended" or "nodeploy" when building like so:
|
||||
# docker build --build-arg HUGO_BUILD_TAGS=extended .
|
||||
@@ -26,7 +26,7 @@ RUN mage hugo && mage install
|
||||
|
||||
# ---
|
||||
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.18
|
||||
|
||||
COPY --from=build /go/bin/hugo /usr/bin/hugo
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[bep]: https://github.com/spf13
|
||||
[bep]: https://github.com/bep
|
||||
[bugs]: https://github.com/gohugoio/hugo/issues?q=is%3Aopen+is%3Aissue+label%3ABug
|
||||
[contributing]: CONTRIBUTING.md
|
||||
[create a proposal]: https://github.com/gohugoio/hugo/issues/new?labels=Proposal%2C+NeedsTriage&template=feature_request.md
|
||||
@@ -33,7 +33,7 @@ A fast and flexible static site generator built with love by [bep], [spf13], and
|
||||
[](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
|
||||
[](https://goreportcard.com/report/github.com/gohugoio/hugo)
|
||||
|
||||
[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | [Twitter]
|
||||
[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a>
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
Vendored
+33
-4
@@ -51,6 +51,9 @@ type Cache struct {
|
||||
pruneAllRootDir string
|
||||
|
||||
nlocker *lockTracker
|
||||
|
||||
initOnce sync.Once
|
||||
initErr error
|
||||
}
|
||||
|
||||
type lockTracker struct {
|
||||
@@ -103,9 +106,23 @@ func (l *lockedFile) Close() error {
|
||||
return l.File.Close()
|
||||
}
|
||||
|
||||
func (c *Cache) init() error {
|
||||
c.initOnce.Do(func() {
|
||||
// Create the base dir if it does not exist.
|
||||
if err := c.Fs.MkdirAll("", 0777); err != nil && !os.IsExist(err) {
|
||||
c.initErr = err
|
||||
}
|
||||
})
|
||||
return c.initErr
|
||||
}
|
||||
|
||||
// WriteCloser returns a transactional writer into the cache.
|
||||
// It's important that it's closed when done.
|
||||
func (c *Cache) WriteCloser(id string) (ItemInfo, io.WriteCloser, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
|
||||
id = cleanID(id)
|
||||
c.nlocker.Lock(id)
|
||||
|
||||
@@ -130,6 +147,10 @@ func (c *Cache) WriteCloser(id string) (ItemInfo, io.WriteCloser, error) {
|
||||
func (c *Cache) ReadOrCreate(id string,
|
||||
read func(info ItemInfo, r io.ReadSeeker) error,
|
||||
create func(info ItemInfo, w io.WriteCloser) error) (info ItemInfo, err error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, err
|
||||
}
|
||||
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -163,6 +184,9 @@ func (c *Cache) ReadOrCreate(id string,
|
||||
// be invoked and the result cached.
|
||||
// This method is protected by a named lock using the given id as identifier.
|
||||
func (c *Cache) GetOrCreate(id string, create func() (io.ReadCloser, error)) (ItemInfo, io.ReadCloser, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -197,6 +221,9 @@ func (c *Cache) GetOrCreate(id string, create func() (io.ReadCloser, error)) (It
|
||||
|
||||
// GetOrCreateBytes is the same as GetOrCreate, but produces a byte slice.
|
||||
func (c *Cache) GetOrCreateBytes(id string, create func() ([]byte, error)) (ItemInfo, []byte, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -232,6 +259,9 @@ func (c *Cache) GetOrCreateBytes(id string, create func() ([]byte, error)) (Item
|
||||
|
||||
// GetBytes gets the file content with the given id from the cache, nil if none found.
|
||||
func (c *Cache) GetBytes(id string) (ItemInfo, []byte, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -250,6 +280,9 @@ func (c *Cache) GetBytes(id string) (ItemInfo, []byte, error) {
|
||||
|
||||
// Get gets the file with the given id from the cache, nil if none found.
|
||||
func (c *Cache) Get(id string) (ItemInfo, io.ReadCloser, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return ItemInfo{}, nil, err
|
||||
}
|
||||
id = cleanID(id)
|
||||
|
||||
c.nlocker.Lock(id)
|
||||
@@ -347,10 +380,6 @@ func NewCaches(p *helpers.PathSpec) (Caches, error) {
|
||||
|
||||
baseDir := v.DirCompiled
|
||||
|
||||
if err := cfs.MkdirAll(baseDir, 0777); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bfs := afero.NewBasePathFs(cfs, baseDir)
|
||||
|
||||
var pruneAllRootDir string
|
||||
|
||||
Vendored
+1
-1
@@ -93,7 +93,7 @@ type FileCacheConfig struct {
|
||||
|
||||
// Will resources/_gen will get its own composite filesystem that
|
||||
// also checks any theme.
|
||||
IsResourceDir bool
|
||||
IsResourceDir bool `json:"-"`
|
||||
}
|
||||
|
||||
// GetJSONCache gets the file cache for getJSON.
|
||||
|
||||
Vendored
+6
@@ -53,6 +53,9 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
if c.pruneAllRootDir != "" {
|
||||
return c.pruneRootDir(force)
|
||||
}
|
||||
if err := c.init(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
counter := 0
|
||||
|
||||
@@ -117,6 +120,9 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
}
|
||||
|
||||
func (c *Cache) pruneRootDir(force bool) (int, error) {
|
||||
if err := c.init(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
info, err := c.Fs.Stat(c.pruneAllRootDir)
|
||||
if err != nil {
|
||||
if herrors.IsNotExist(err) {
|
||||
|
||||
+12
-19
@@ -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,26 +429,26 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
optsLogger := loggers.Options{
|
||||
Distinct: true,
|
||||
Level: level,
|
||||
Stdout: r.Out,
|
||||
Stderr: r.Out,
|
||||
StoreErrors: running,
|
||||
DistinctLevel: logg.LevelWarn,
|
||||
Level: level,
|
||||
Stdout: r.Out,
|
||||
Stderr: r.Out,
|
||||
StoreErrors: running,
|
||||
}
|
||||
|
||||
return loggers.New(optsLogger), nil
|
||||
|
||||
}
|
||||
|
||||
func (r *rootCommand) Reset() {
|
||||
@@ -515,11 +510,10 @@ Complete documentation is available at https://gohugo.io/.`
|
||||
func applyLocalFlagsBuildConfig(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)")
|
||||
cmd.Flags().StringVarP(&r.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. https://spf13.com/")
|
||||
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/")
|
||||
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory")
|
||||
_ = 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) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
@@ -92,6 +93,7 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
if err := json.Unmarshal(buf.Bytes(), &m); err != nil {
|
||||
return err
|
||||
}
|
||||
maps.ConvertFloat64WithNoDecimalsToInt(m)
|
||||
switch format {
|
||||
case "yaml":
|
||||
return parser.InterfaceToConfig(m, metadecoders.YAML, os.Stdout)
|
||||
|
||||
+30
-8
@@ -14,6 +14,7 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -30,8 +31,11 @@ import (
|
||||
"github.com/gohugoio/hugo/docshelper"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func newGenCommand() *genCommand {
|
||||
@@ -72,7 +76,7 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.PersistentFlags().StringVar(&style, "style", "friendly", "highlighter style (see https://xyproto.github.io/splash/docs/)")
|
||||
cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "bg:#ffffcc", "style used for highlighting lines (see https://github.com/alecthomas/chroma)")
|
||||
cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "", "style used for highlighting lines (see https://github.com/alecthomas/chroma)")
|
||||
cmd.PersistentFlags().StringVar(&linesStyle, "linesStyle", "", "style used for line numbers (see https://github.com/alecthomas/chroma)")
|
||||
},
|
||||
}
|
||||
@@ -132,7 +136,7 @@ url: %s
|
||||
long: `Generate Markdown documentation for the Hugo CLI.
|
||||
This command is, mostly, used to create up-to-date documentation
|
||||
of Hugo's command-line interface for https://gohugo.io/.
|
||||
|
||||
|
||||
It creates one Markdown file per command with front matter suitable
|
||||
for rendering in Hugo.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
@@ -186,18 +190,36 @@ url: %s
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
r.Println("Generate docs data to", docsHelperTarget)
|
||||
|
||||
targetFile := filepath.Join(docsHelperTarget, "docs.json")
|
||||
var buf bytes.Buffer
|
||||
jsonEnc := json.NewEncoder(&buf)
|
||||
|
||||
configProvider := func() docshelper.DocProvider {
|
||||
conf := hugolib.DefaultConfig()
|
||||
conf.CacheDir = "" // The default value does not make sense in the docs.
|
||||
defaultConfig := parser.LowerCaseCamelJSONMarshaller{Value: conf}
|
||||
return docshelper.DocProvider{"config": defaultConfig}
|
||||
}
|
||||
|
||||
docshelper.AddDocProviderFunc(configProvider)
|
||||
if err := jsonEnc.Encode(docshelper.GetDocProvider()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode the JSON to a map[string]interface{} and then unmarshal it again to the correct format.
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(buf.Bytes(), &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetFile := filepath.Join(docsHelperTarget, "docs.yaml")
|
||||
|
||||
f, err := os.Create(targetFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
enc := json.NewEncoder(f)
|
||||
enc.SetIndent("", " ")
|
||||
|
||||
if err := enc.Encode(docshelper.GetDocProvider()); err != nil {
|
||||
yamlEnc := yaml.NewEncoder(f)
|
||||
if err := yamlEnc.Encode(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+6
-18
@@ -14,7 +14,6 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -24,8 +23,6 @@ import (
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
@@ -86,13 +83,12 @@ func flagsToCfgWithAdditionalConfigBase(cd *simplecobra.Commandeer, cfg config.P
|
||||
|
||||
// Flags that we for some reason don't want to expose in the site config.
|
||||
internalKeySet := map[string]bool{
|
||||
"quiet": true,
|
||||
"verbose": true,
|
||||
"watch": true,
|
||||
"disableLiveReload": true,
|
||||
"liveReloadPort": true,
|
||||
"renderToMemory": true,
|
||||
"clock": true,
|
||||
"quiet": true,
|
||||
"verbose": true,
|
||||
"watch": true,
|
||||
"liveReloadPort": true,
|
||||
"renderToMemory": true,
|
||||
"clock": true,
|
||||
}
|
||||
|
||||
cmd := cd.CobraCommand
|
||||
@@ -124,11 +120,3 @@ func mkdir(x ...string) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func touchFile(fs afero.Fs, filename string) {
|
||||
mkdir(filepath.Dir(filename))
|
||||
err := helpers.WriteToDisk(filename, bytes.NewReader([]byte{}), fs)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ func init() {
|
||||
// This message to show to Windows users if Hugo is opened from explorer.exe
|
||||
cobra.MousetrapHelpText = `
|
||||
|
||||
Hugo is a command-line tool for generating static website.
|
||||
Hugo is a command-line tool for generating static websites.
|
||||
|
||||
You need to open PowerShell and run Hugo from there.
|
||||
|
||||
You need to open cmd.exe and run Hugo from there.
|
||||
|
||||
Visit https://gohugo.io/ for more information.`
|
||||
}
|
||||
|
||||
+7
-3
@@ -211,7 +211,12 @@ Install a specific version:
|
||||
|
||||
hugo mod get github.com/gohugoio/testshortcodes@v0.3.0
|
||||
|
||||
Install the latest versions of all module dependencies:
|
||||
Install the latest versions of all direct module dependencies:
|
||||
|
||||
hugo mod get
|
||||
hugo mod get ./... (recursive)
|
||||
|
||||
Install the latest versions of all module dependencies (direct and indirect):
|
||||
|
||||
hugo mod get -u
|
||||
hugo mod get -u ./... (recursive)
|
||||
@@ -281,7 +286,6 @@ Run "go help get" for more information. All flags available for "go get" is also
|
||||
npmCommand,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type modCommands struct {
|
||||
@@ -303,7 +307,7 @@ func (c *modCommands) Run(ctx context.Context, cd *simplecobra.Commandeer, args
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//config := conf.configs.Base
|
||||
// config := conf.configs.Base
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+26
-192
@@ -16,19 +16,14 @@ package commands
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/gohugoio/hugo/common/htime"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/create"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/gohugoio/hugo/create/skeletons"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -68,7 +63,6 @@ Ensure you run this within the root directory of your site.`,
|
||||
cmd.Flags().StringVarP(&contentType, "kind", "k", "", "content type to create")
|
||||
cmd.Flags().String("editor", "", "edit new content with this editor, if provided")
|
||||
cmd.Flags().BoolVarP(&force, "force", "f", false, "overwrite file if it already exists")
|
||||
cmd.Flags().StringVar(&format, "format", "toml", "preferred file format (toml, yaml or json)")
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
|
||||
},
|
||||
@@ -99,52 +93,13 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
}
|
||||
sourceFs := conf.fs.Source
|
||||
|
||||
archeTypePath := filepath.Join(createpath, "archetypes")
|
||||
dirs := []string{
|
||||
archeTypePath,
|
||||
filepath.Join(createpath, "assets"),
|
||||
filepath.Join(createpath, "content"),
|
||||
filepath.Join(createpath, "data"),
|
||||
filepath.Join(createpath, "layouts"),
|
||||
filepath.Join(createpath, "static"),
|
||||
filepath.Join(createpath, "themes"),
|
||||
err = skeletons.CreateSite(createpath, sourceFs, force, format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists, _ := helpers.Exists(createpath, sourceFs); exists {
|
||||
if isDir, _ := helpers.IsDir(createpath, sourceFs); !isDir {
|
||||
return errors.New(createpath + " already exists but not a directory")
|
||||
}
|
||||
|
||||
isEmpty, _ := helpers.IsEmpty(createpath, sourceFs)
|
||||
|
||||
switch {
|
||||
case !isEmpty && !force:
|
||||
return errors.New(createpath + " already exists and is not empty. See --force.")
|
||||
|
||||
case !isEmpty && force:
|
||||
all := append(dirs, filepath.Join(createpath, "hugo."+format))
|
||||
for _, path := range all {
|
||||
if exists, _ := helpers.Exists(path, sourceFs); exists {
|
||||
return errors.New(path + " already exists")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
if err := sourceFs.MkdirAll(dir, 0777); err != nil {
|
||||
return fmt.Errorf("failed to create dir: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.newSiteCreateConfig(sourceFs, createpath, format)
|
||||
|
||||
// Create a default archetype file.
|
||||
helpers.SafeWriteToDisk(filepath.Join(archeTypePath, "default.md"),
|
||||
strings.NewReader(create.DefaultArchetypeTemplateTemplate), sourceFs)
|
||||
|
||||
r.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", createpath)
|
||||
r.Println(c.newSiteNextStepsText())
|
||||
r.Printf("Congratulations! Your new Hugo site was created in %s.\n\n", createpath)
|
||||
r.Println(c.newSiteNextStepsText(createpath, format))
|
||||
|
||||
return nil
|
||||
},
|
||||
@@ -165,91 +120,22 @@ according to your needs.`,
|
||||
if len(args) < 1 {
|
||||
return newUserError("theme name needs to be provided")
|
||||
}
|
||||
h, err := r.Hugo(flagsToCfg(cd, nil))
|
||||
cfg := config.New()
|
||||
cfg.Set("publishDir", "public")
|
||||
|
||||
conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ps := h.PathSpec
|
||||
sourceFs := ps.Fs.Source
|
||||
themesDir := h.Configs.LoadingInfo.BaseConfig.ThemesDir
|
||||
createpath := ps.AbsPathify(filepath.Join(themesDir, args[0]))
|
||||
r.Println("Creating theme at", createpath)
|
||||
sourceFs := conf.fs.Source
|
||||
createpath := paths.AbsPathify(conf.configs.Base.WorkingDir, filepath.Join(conf.configs.Base.ThemesDir, args[0]))
|
||||
r.Println("Creating new theme in", createpath)
|
||||
|
||||
if x, _ := helpers.Exists(createpath, sourceFs); x {
|
||||
return errors.New(createpath + " already exists")
|
||||
}
|
||||
|
||||
for _, filename := range []string{
|
||||
"index.html",
|
||||
"404.html",
|
||||
"_default/list.html",
|
||||
"_default/single.html",
|
||||
"partials/head.html",
|
||||
"partials/header.html",
|
||||
"partials/footer.html",
|
||||
} {
|
||||
touchFile(sourceFs, filepath.Join(createpath, "layouts", filename))
|
||||
}
|
||||
|
||||
baseofDefault := []byte(`<!DOCTYPE html>
|
||||
<html>
|
||||
{{- partial "head.html" . -}}
|
||||
<body>
|
||||
{{- partial "header.html" . -}}
|
||||
<div id="content">
|
||||
{{- block "main" . }}{{- end }}
|
||||
</div>
|
||||
{{- partial "footer.html" . -}}
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "layouts", "_default", "baseof.html"), bytes.NewReader(baseofDefault), sourceFs)
|
||||
err = skeletons.CreateTheme(createpath, sourceFs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mkdir(createpath, "archetypes")
|
||||
|
||||
archDefault := []byte("+++\n+++\n")
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), sourceFs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mkdir(createpath, "static", "js")
|
||||
mkdir(createpath, "static", "css")
|
||||
|
||||
by := []byte(`The MIT License (MIT)
|
||||
|
||||
Copyright (c) ` + htime.Now().Format("2006") + ` YOUR_NAME_HERE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE"), bytes.NewReader(by), sourceFs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.createThemeMD(ps.Fs.Source, createpath)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
@@ -299,77 +185,25 @@ func (c *newCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *newCommand) newSiteCreateConfig(fs afero.Fs, inpath string, kind string) (err error) {
|
||||
in := map[string]string{
|
||||
"baseURL": "http://example.org/",
|
||||
"title": "My New Hugo Site",
|
||||
"languageCode": "en-us",
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = parser.InterfaceToConfig(in, metadecoders.FormatFromString(kind), &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return helpers.WriteToDisk(filepath.Join(inpath, "hugo."+kind), &buf, fs)
|
||||
}
|
||||
|
||||
func (c *newCommand) newSiteNextStepsText() string {
|
||||
func (c *newCommand) newSiteNextStepsText(path string, format string) string {
|
||||
format = strings.ToLower(format)
|
||||
var nextStepsText bytes.Buffer
|
||||
|
||||
nextStepsText.WriteString(`Just a few more steps and you're ready to go:
|
||||
nextStepsText.WriteString(`Just a few more steps...
|
||||
|
||||
1. Download a theme into the same-named folder.
|
||||
Choose a theme from https://themes.gohugo.io/ or
|
||||
create your own with the "hugo new theme <THEMENAME>" command.
|
||||
2. Perhaps you want to add some content. You can add single files
|
||||
with "hugo new `)
|
||||
1. Change the current directory to ` + path + `.
|
||||
2. Create or install a theme:
|
||||
- Create a new theme with the command "hugo new theme <THEMENAME>"
|
||||
- Install a theme from https://themes.gohugo.io/
|
||||
3. Edit hugo.` + format + `, setting the "theme" property to the theme name.
|
||||
4. Create new content with the command "hugo new content `)
|
||||
|
||||
nextStepsText.WriteString(filepath.Join("<SECTIONNAME>", "<FILENAME>.<FORMAT>"))
|
||||
|
||||
nextStepsText.WriteString(`".
|
||||
3. Start the built-in live server via "hugo server".
|
||||
5. Start the embedded web server with the command "hugo server --buildDrafts".
|
||||
|
||||
Visit https://gohugo.io/ for quickstart guide and full documentation.`)
|
||||
See documentation at https://gohugo.io/.`)
|
||||
|
||||
return nextStepsText.String()
|
||||
}
|
||||
|
||||
func (c *newCommand) createThemeMD(fs afero.Fs, inpath string) (err error) {
|
||||
|
||||
by := []byte(`# theme.toml template for a Hugo theme
|
||||
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
|
||||
|
||||
name = "` + strings.Title(helpers.MakeTitle(filepath.Base(inpath))) + `"
|
||||
license = "MIT"
|
||||
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE"
|
||||
description = ""
|
||||
homepage = "http://example.com/"
|
||||
tags = []
|
||||
features = []
|
||||
min_version = "0.115.0"
|
||||
|
||||
[author]
|
||||
name = ""
|
||||
homepage = ""
|
||||
|
||||
# If porting an existing theme
|
||||
[original]
|
||||
name = ""
|
||||
homepage = ""
|
||||
repo = ""
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), fs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(inpath, "hugo.toml"), strings.NewReader("# Theme config.\n"), fs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+12
-12
@@ -23,7 +23,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -470,14 +469,6 @@ func (c *serverCommand) Name() string {
|
||||
}
|
||||
|
||||
func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
|
||||
err := func() error {
|
||||
defer c.r.timeTrack(time.Now(), "Built")
|
||||
err := c.build()
|
||||
return err
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Watch runs its own server as part of the routine
|
||||
if c.serverWatch {
|
||||
@@ -501,6 +492,15 @@ func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
|
||||
}
|
||||
|
||||
err := func() error {
|
||||
defer c.r.timeTrack(time.Now(), "Built")
|
||||
err := c.build()
|
||||
return err
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.serve()
|
||||
}
|
||||
|
||||
@@ -685,9 +685,9 @@ func (c *serverCommand) createCertificates(conf *commonConfig) error {
|
||||
c.tlsKeyFile = filepath.Join(keyDir, fmt.Sprintf("%s-key.pem", hostname))
|
||||
|
||||
// Check if the certificate already exists and is valid.
|
||||
certPEM, err := ioutil.ReadFile(c.tlsCertFile)
|
||||
certPEM, err := os.ReadFile(c.tlsCertFile)
|
||||
if err == nil {
|
||||
rootPem, err := ioutil.ReadFile(filepath.Join(mclib.GetCAROOT(), "rootCA.pem"))
|
||||
rootPem, err := os.ReadFile(filepath.Join(mclib.GetCAROOT(), "rootCA.pem"))
|
||||
if err == nil {
|
||||
if err := c.verifyCert(rootPem, certPEM, hostname); err == nil {
|
||||
c.r.Println("Using existing", c.tlsCertFile, "and", c.tlsKeyFile)
|
||||
@@ -954,7 +954,7 @@ func (c *serverCommand) serve() error {
|
||||
mu.HandleFunc(u.Path+"/livereload.js", livereload.ServeJS)
|
||||
mu.HandleFunc(u.Path+"/livereload", livereload.Handler)
|
||||
}
|
||||
c.r.Printf("Web Server is available at %s (bind address %s)\n", serverURL, c.serverInterface)
|
||||
c.r.Printf("Web Server is available at %s (bind address %s) %s\n", serverURL, c.serverInterface, roots[i])
|
||||
wg1.Go(func() error {
|
||||
if c.tlsCertFile != "" && c.tlsKeyFile != "" {
|
||||
err = srv.ServeTLS(listener, c.tlsCertFile, c.tlsKeyFile)
|
||||
|
||||
@@ -16,9 +16,6 @@ package constants
|
||||
// Error IDs.
|
||||
// Do not change these values.
|
||||
const (
|
||||
ErrIDAmbigousDisableKindTaxonomy = "error-disable-taxonomy"
|
||||
ErrIDAmbigousOutputKindTaxonomy = "error-output-taxonomy"
|
||||
|
||||
// IDs for remote errors in tpl/data.
|
||||
ErrRemoteGetJSON = "error-remote-getjson"
|
||||
ErrRemoteGetCSV = "error-remote-getcsv"
|
||||
|
||||
@@ -59,11 +59,34 @@ func GetGID() uint64 {
|
||||
return n
|
||||
}
|
||||
|
||||
// IsFeatureNotAvailableError returns true if the given error is or contains a FeatureNotAvailableError.
|
||||
func IsFeatureNotAvailableError(err error) bool {
|
||||
return errors.Is(err, &FeatureNotAvailableError{})
|
||||
}
|
||||
|
||||
// ErrFeatureNotAvailable denotes that a feature is unavailable.
|
||||
//
|
||||
// We will, at least to begin with, make some Hugo features (SCSS with libsass) optional,
|
||||
// and this error is used to signal those situations.
|
||||
var ErrFeatureNotAvailable = errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")
|
||||
var ErrFeatureNotAvailable = &FeatureNotAvailableError{Cause: errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")}
|
||||
|
||||
// FeatureNotAvailableError is an error type used to signal that a feature is not available.
|
||||
type FeatureNotAvailableError struct {
|
||||
Cause error
|
||||
}
|
||||
|
||||
func (e *FeatureNotAvailableError) Unwrap() error {
|
||||
return e.Cause
|
||||
}
|
||||
|
||||
func (e *FeatureNotAvailableError) Error() string {
|
||||
return e.Cause.Error()
|
||||
}
|
||||
|
||||
func (e *FeatureNotAvailableError) Is(target error) bool {
|
||||
_, ok := target.(*FeatureNotAvailableError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Must panics if err != nil.
|
||||
func Must(err error) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package herrors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
@@ -34,3 +35,12 @@ func TestIsNotExist(t *testing.T) {
|
||||
// os.IsNotExist returns false for wrapped errors.
|
||||
c.Assert(IsNotExist(fmt.Errorf("foo: %w", afero.ErrFileNotFound)), qt.Equals, true)
|
||||
}
|
||||
|
||||
func TestIsFeatureNotAvailableError(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(IsFeatureNotAvailableError(ErrFeatureNotAvailable), qt.Equals, true)
|
||||
c.Assert(IsFeatureNotAvailableError(&FeatureNotAvailableError{}), qt.Equals, true)
|
||||
c.Assert(IsFeatureNotAvailableError(errors.New("asdf")), qt.Equals, false)
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ package hstrings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gohugoio/hugo/compare"
|
||||
)
|
||||
@@ -55,3 +57,68 @@ func EqualAny(a string, b ...string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// regexpCache represents a cache of regexp objects protected by a mutex.
|
||||
type regexpCache struct {
|
||||
mu sync.RWMutex
|
||||
re map[string]*regexp.Regexp
|
||||
}
|
||||
|
||||
func (rc *regexpCache) getOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) {
|
||||
var ok bool
|
||||
|
||||
if re, ok = rc.get(pattern); !ok {
|
||||
re, err = regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc.set(pattern, re)
|
||||
}
|
||||
|
||||
return re, nil
|
||||
}
|
||||
|
||||
func (rc *regexpCache) get(key string) (re *regexp.Regexp, ok bool) {
|
||||
rc.mu.RLock()
|
||||
re, ok = rc.re[key]
|
||||
rc.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (rc *regexpCache) set(key string, re *regexp.Regexp) {
|
||||
rc.mu.Lock()
|
||||
rc.re[key] = re
|
||||
rc.mu.Unlock()
|
||||
}
|
||||
|
||||
var reCache = regexpCache{re: make(map[string]*regexp.Regexp)}
|
||||
|
||||
// GetOrCompileRegexp retrieves a regexp object from the cache based upon the pattern.
|
||||
// If the pattern is not found in the cache, the pattern is compiled and added to
|
||||
// the cache.
|
||||
func GetOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) {
|
||||
return reCache.getOrCompileRegexp(pattern)
|
||||
}
|
||||
|
||||
// InSlice checks if a string is an element of a slice of strings
|
||||
// and returns a boolean value.
|
||||
func InSlice(arr []string, el string) bool {
|
||||
for _, v := range arr {
|
||||
if v == el {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InSlicEqualFold checks if a string is an element of a slice of strings
|
||||
// and returns a boolean value.
|
||||
// It uses strings.EqualFold to compare.
|
||||
func InSlicEqualFold(arr []string, el string) bool {
|
||||
for _, v := range arr {
|
||||
if strings.EqualFold(v, el) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package hstrings
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
@@ -34,3 +35,24 @@ func TestStringEqualFold(t *testing.T) {
|
||||
c.Assert(StringEqualFold(s1).Eq("b"), qt.Equals, false)
|
||||
|
||||
}
|
||||
|
||||
func TestGetOrCompileRegexp(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
re, err := GetOrCompileRegexp(`\d+`)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(re.MatchString("123"), qt.Equals, true)
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkGetOrCompileRegexp(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
GetOrCompileRegexp(`\d+`)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompileRegexp(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
regexp.MustCompile(`\d+`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2022 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 htime_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
// Issue #11267
|
||||
func TestApplyWithContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
defaultContentLanguage = 'it'
|
||||
-- layouts/index.html --
|
||||
{{ $dates := slice
|
||||
"2022-01-03"
|
||||
"2022-02-01"
|
||||
"2022-03-02"
|
||||
"2022-04-07"
|
||||
"2022-05-06"
|
||||
"2022-06-04"
|
||||
"2022-07-03"
|
||||
"2022-08-01"
|
||||
"2022-09-06"
|
||||
"2022-10-05"
|
||||
"2022-11-03"
|
||||
"2022-12-02"
|
||||
}}
|
||||
{{ range $dates }}
|
||||
{{ . | time.Format "month: _January_ weekday: _Monday_" }}
|
||||
{{ . | time.Format "month: _Jan_ weekday: _Mon_" }}
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
month: _gennaio_ weekday: _lunedì_
|
||||
month: _gen_ weekday: _lun_
|
||||
month: _febbraio_ weekday: _martedì_
|
||||
month: _feb_ weekday: _mar_
|
||||
month: _marzo_ weekday: _mercoledì_
|
||||
month: _mar_ weekday: _mer_
|
||||
month: _aprile_ weekday: _giovedì_
|
||||
month: _apr_ weekday: _gio_
|
||||
month: _maggio_ weekday: _venerdì_
|
||||
month: _mag_ weekday: _ven_
|
||||
month: _giugno_ weekday: _sabato_
|
||||
month: _giu_ weekday: _sab_
|
||||
month: _luglio_ weekday: _domenica_
|
||||
month: _lug_ weekday: _dom_
|
||||
month: _agosto_ weekday: _lunedì_
|
||||
month: _ago_ weekday: _lun_
|
||||
month: _settembre_ weekday: _martedì_
|
||||
month: _set_ weekday: _mar_
|
||||
month: _ottobre_ weekday: _mercoledì_
|
||||
month: _ott_ weekday: _mer_
|
||||
month: _novembre_ weekday: _giovedì_
|
||||
month: _nov_ weekday: _gio_
|
||||
month: _dicembre_ weekday: _venerdì_
|
||||
month: _dic_ weekday: _ven_
|
||||
`)
|
||||
}
|
||||
@@ -124,12 +124,15 @@ func (f TimeFormatter) Format(t time.Time, layout string) string {
|
||||
monthIdx := t.Month() - 1 // Month() starts at 1.
|
||||
dayIdx := t.Weekday()
|
||||
|
||||
s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthWide(t.Month()))
|
||||
if !strings.Contains(s, f.ltr.MonthWide(t.Month())) {
|
||||
if strings.Contains(layout, "January") {
|
||||
s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthWide(t.Month()))
|
||||
} else if strings.Contains(layout, "Jan") {
|
||||
s = strings.ReplaceAll(s, shortMonthNames[monthIdx], f.ltr.MonthAbbreviated(t.Month()))
|
||||
}
|
||||
s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdayWide(t.Weekday()))
|
||||
if !strings.Contains(s, f.ltr.WeekdayWide(t.Weekday())) {
|
||||
|
||||
if strings.Contains(layout, "Monday") {
|
||||
s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdayWide(t.Weekday()))
|
||||
} else if strings.Contains(layout, "Mon") {
|
||||
s = strings.ReplaceAll(s, shortDayNames[dayIdx], f.ltr.WeekdayAbbreviated(t.Weekday()))
|
||||
}
|
||||
|
||||
|
||||
+63
-6
@@ -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"
|
||||
@@ -78,10 +79,22 @@ func (i HugoInfo) Generator() template.HTML {
|
||||
return template.HTML(fmt.Sprintf(`<meta name="generator" content="Hugo %s">`, CurrentVersion.String()))
|
||||
}
|
||||
|
||||
// IsDevelopment reports whether the current running environment is "development".
|
||||
func (i HugoInfo) IsDevelopment() bool {
|
||||
return i.Environment == EnvironmentDevelopment
|
||||
}
|
||||
|
||||
// IsProduction reports whether the current running environment is "production".
|
||||
func (i HugoInfo) IsProduction() bool {
|
||||
return i.Environment == EnvironmentProduction
|
||||
}
|
||||
|
||||
// IsServer reports whether the built-in server is running.
|
||||
func (i HugoInfo) IsServer() bool {
|
||||
return i.conf.Running()
|
||||
}
|
||||
|
||||
// IsExtended reports whether the Hugo binary is the extended version.
|
||||
func (i HugoInfo) IsExtended() bool {
|
||||
return IsExtended
|
||||
}
|
||||
@@ -99,6 +112,7 @@ func (i HugoInfo) Deps() []*Dependency {
|
||||
// ConfigProvider represents the config options that are relevant for HugoInfo.
|
||||
type ConfigProvider interface {
|
||||
Environment() string
|
||||
Running() bool
|
||||
WorkingDir() string
|
||||
}
|
||||
|
||||
@@ -170,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() {
|
||||
@@ -198,7 +214,6 @@ func getBuildInfo() *buildInfo {
|
||||
bInfo.GoArch = s.Value
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return bInfo
|
||||
@@ -242,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"
|
||||
}
|
||||
@@ -333,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).WithField(loggers.FieldNameCmd, "deprecated").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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,14 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/bep/logg"
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestHugoInfo(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
conf := testConfig{environment: "production", workingDir: "/mywork"}
|
||||
conf := testConfig{environment: "production", workingDir: "/mywork", running: false}
|
||||
hugoInfo := NewInfo(conf, nil)
|
||||
|
||||
c.Assert(hugoInfo.Version(), qt.Equals, CurrentVersion.Version())
|
||||
@@ -38,15 +39,34 @@ func TestHugoInfo(t *testing.T) {
|
||||
}
|
||||
c.Assert(hugoInfo.Environment, qt.Equals, "production")
|
||||
c.Assert(string(hugoInfo.Generator()), qt.Contains, fmt.Sprintf("Hugo %s", hugoInfo.Version()))
|
||||
c.Assert(hugoInfo.IsDevelopment(), qt.Equals, false)
|
||||
c.Assert(hugoInfo.IsProduction(), qt.Equals, true)
|
||||
c.Assert(hugoInfo.IsExtended(), qt.Equals, IsExtended)
|
||||
c.Assert(hugoInfo.IsServer(), qt.Equals, false)
|
||||
|
||||
devHugoInfo := NewInfo(testConfig{environment: "development"}, nil)
|
||||
devHugoInfo := NewInfo(testConfig{environment: "development", running: true}, nil)
|
||||
c.Assert(devHugoInfo.IsDevelopment(), qt.Equals, true)
|
||||
c.Assert(devHugoInfo.IsProduction(), qt.Equals, false)
|
||||
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
|
||||
workingDir string
|
||||
}
|
||||
|
||||
@@ -54,6 +74,10 @@ func (c testConfig) Environment() string {
|
||||
return c.environment
|
||||
}
|
||||
|
||||
func (c testConfig) Running() bool {
|
||||
return c.running
|
||||
}
|
||||
|
||||
func (c testConfig) WorkingDir() string {
|
||||
return c.workingDir
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 116,
|
||||
Minor: 121,
|
||||
PatchLevel: 0,
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
||||
+25
-15
@@ -37,13 +37,13 @@ var (
|
||||
|
||||
// Options defines options for the logger.
|
||||
type Options struct {
|
||||
Level logg.Level
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Distinct bool
|
||||
StoreErrors bool
|
||||
HandlerPost func(e *logg.Entry) error
|
||||
SuppresssStatements map[string]bool
|
||||
Level logg.Level
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
DistinctLevel logg.Level
|
||||
StoreErrors bool
|
||||
HandlerPost func(e *logg.Entry) error
|
||||
SuppressStatements map[string]bool
|
||||
}
|
||||
|
||||
// New creates a new logger with the given options.
|
||||
@@ -92,13 +92,13 @@ func New(opts Options) Logger {
|
||||
logHandler = multi.New(handlers...)
|
||||
|
||||
var logOnce *logOnceHandler
|
||||
if opts.Distinct {
|
||||
logOnce = newLogOnceHandler(logg.LevelWarn)
|
||||
if opts.DistinctLevel != 0 {
|
||||
logOnce = newLogOnceHandler(opts.DistinctLevel)
|
||||
logHandler = newStopHandler(logOnce, logHandler)
|
||||
}
|
||||
|
||||
if opts.SuppresssStatements != nil && len(opts.SuppresssStatements) > 0 {
|
||||
logHandler = newStopHandler(newSuppressStatementsHandler(opts.SuppresssStatements), logHandler)
|
||||
if opts.SuppressStatements != nil && len(opts.SuppressStatements) > 0 {
|
||||
logHandler = newStopHandler(newSuppressStatementsHandler(opts.SuppressStatements), logHandler)
|
||||
}
|
||||
|
||||
logger := logg.New(
|
||||
@@ -137,10 +137,10 @@ func New(opts Options) Logger {
|
||||
// NewDefault creates a new logger with the default options.
|
||||
func NewDefault() Logger {
|
||||
opts := Options{
|
||||
Distinct: true,
|
||||
Level: logg.LevelWarn,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stdout,
|
||||
DistinctLevel: logg.LevelWarn,
|
||||
Level: logg.LevelWarn,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stdout,
|
||||
}
|
||||
return New(opts)
|
||||
}
|
||||
@@ -173,6 +173,7 @@ type Logger interface {
|
||||
WarnCommand(command string) logg.LevelLogger
|
||||
Warnf(format string, v ...any)
|
||||
Warnln(v ...any)
|
||||
Deprecatef(fail bool, format string, v ...any)
|
||||
}
|
||||
|
||||
type logAdapter struct {
|
||||
@@ -297,6 +298,15 @@ func (l *logAdapter) sprint(v ...any) string {
|
||||
return strings.TrimRight(fmt.Sprintln(v...), "\n")
|
||||
}
|
||||
|
||||
func (l *logAdapter) Deprecatef(fail bool, format string, v ...any) {
|
||||
format = "DEPRECATED: " + format
|
||||
if fail {
|
||||
l.errorl.Logf(format, v...)
|
||||
} else {
|
||||
l.warnl.Logf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
type logWriter struct {
|
||||
l logg.LevelLogger
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@ func TestLogDistinct(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
opts := loggers.Options{
|
||||
Distinct: true,
|
||||
StoreErrors: true,
|
||||
Stdout: io.Discard,
|
||||
Stderr: io.Discard,
|
||||
DistinctLevel: logg.LevelWarn,
|
||||
StoreErrors: true,
|
||||
Stdout: io.Discard,
|
||||
Stderr: io.Discard,
|
||||
}
|
||||
|
||||
l := loggers.New(opts)
|
||||
@@ -85,7 +85,6 @@ func TestOptionStoreErrors(t *testing.T) {
|
||||
|
||||
c.Assert(sb.String(), qt.Contains, "error 1")
|
||||
c.Assert(sb.String(), qt.Contains, "ERROR")
|
||||
|
||||
}
|
||||
|
||||
func TestLogCount(t *testing.T) {
|
||||
@@ -110,7 +109,7 @@ func TestSuppressStatements(t *testing.T) {
|
||||
|
||||
opts := loggers.Options{
|
||||
StoreErrors: true,
|
||||
SuppresssStatements: map[string]bool{
|
||||
SuppressStatements: map[string]bool{
|
||||
"error-1": true,
|
||||
},
|
||||
}
|
||||
@@ -124,17 +123,16 @@ func TestSuppressStatements(t *testing.T) {
|
||||
c.Assert(errorsStr, qt.Not(qt.Contains), "error 1")
|
||||
c.Assert(errorsStr, qt.Contains, "error 2")
|
||||
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 1)
|
||||
|
||||
}
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
opts := loggers.Options{
|
||||
StoreErrors: true,
|
||||
Distinct: true,
|
||||
Stdout: io.Discard,
|
||||
Stderr: io.Discard,
|
||||
StoreErrors: true,
|
||||
DistinctLevel: logg.LevelWarn,
|
||||
Stdout: io.Discard,
|
||||
Stderr: io.Discard,
|
||||
}
|
||||
|
||||
l := loggers.New(opts)
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/bep/logg"
|
||||
)
|
||||
|
||||
func InitGlobalLogger(panicOnWarnings bool) {
|
||||
func InitGlobalLogger(level logg.Level, panicOnWarnings bool) {
|
||||
logMu.Lock()
|
||||
defer logMu.Unlock()
|
||||
var logHookLast func(e *logg.Entry) error
|
||||
@@ -31,8 +31,9 @@ func InitGlobalLogger(panicOnWarnings bool) {
|
||||
|
||||
log = New(
|
||||
Options{
|
||||
Distinct: true,
|
||||
HandlerPost: logHookLast,
|
||||
Level: level,
|
||||
DistinctLevel: logg.LevelInfo,
|
||||
HandlerPost: logHookLast,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -49,5 +50,5 @@ func Log() Logger {
|
||||
var log Logger
|
||||
|
||||
func init() {
|
||||
InitGlobalLogger(false)
|
||||
InitGlobalLogger(logg.LevelWarn, false)
|
||||
}
|
||||
|
||||
@@ -210,3 +210,28 @@ func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertFloat64WithNoDecimalsToInt converts float64 values with no decimals to int recursively.
|
||||
func ConvertFloat64WithNoDecimalsToInt(m map[string]any) {
|
||||
for k, v := range m {
|
||||
switch vv := v.(type) {
|
||||
case float64:
|
||||
if v == float64(int64(vv)) {
|
||||
m[k] = int64(vv)
|
||||
}
|
||||
case map[string]any:
|
||||
ConvertFloat64WithNoDecimalsToInt(vv)
|
||||
case []any:
|
||||
for i, vvv := range vv {
|
||||
switch vvvv := vvv.(type) {
|
||||
case float64:
|
||||
if vvv == float64(int64(vvvv)) {
|
||||
vv[i] = int64(vvvv)
|
||||
}
|
||||
case map[string]any:
|
||||
ConvertFloat64WithNoDecimalsToInt(vvvv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-9
@@ -51,9 +51,10 @@ var pb pathBridge
|
||||
|
||||
// MakePermalink combines base URL with content path to create full URL paths.
|
||||
// Example
|
||||
// base: http://spf13.com/
|
||||
// path: post/how-i-blog
|
||||
// result: http://spf13.com/post/how-i-blog
|
||||
//
|
||||
// base: http://spf13.com/
|
||||
// path: post/how-i-blog
|
||||
// result: http://spf13.com/post/how-i-blog
|
||||
func MakePermalink(host, plink string) *url.URL {
|
||||
base, err := url.Parse(host)
|
||||
if err != nil {
|
||||
@@ -117,17 +118,19 @@ func PrettifyURL(in string) string {
|
||||
|
||||
// PrettifyURLPath takes a URL path to a content and converts it
|
||||
// to enable pretty URLs.
|
||||
// /section/name.html becomes /section/name/index.html
|
||||
// /section/name/ becomes /section/name/index.html
|
||||
// /section/name/index.html becomes /section/name/index.html
|
||||
//
|
||||
// /section/name.html becomes /section/name/index.html
|
||||
// /section/name/ becomes /section/name/index.html
|
||||
// /section/name/index.html becomes /section/name/index.html
|
||||
func PrettifyURLPath(in string) string {
|
||||
return prettifyPath(in, pb)
|
||||
}
|
||||
|
||||
// Uglify does the opposite of PrettifyURLPath().
|
||||
// /section/name/index.html becomes /section/name.html
|
||||
// /section/name/ becomes /section/name.html
|
||||
// /section/name.html becomes /section/name.html
|
||||
//
|
||||
// /section/name/index.html becomes /section/name.html
|
||||
// /section/name/ becomes /section/name.html
|
||||
// /section/name.html becomes /section/name.html
|
||||
func Uglify(in string) string {
|
||||
if path.Ext(in) == "" {
|
||||
if len(in) < 2 {
|
||||
|
||||
@@ -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"
|
||||
@@ -45,6 +46,7 @@ import (
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/related"
|
||||
"github.com/gohugoio/hugo/resources/images"
|
||||
"github.com/gohugoio/hugo/resources/kinds"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/page/pagemeta"
|
||||
"github.com/spf13/afero"
|
||||
@@ -57,12 +59,11 @@ type InternalConfig struct {
|
||||
// Server mode?
|
||||
Running bool
|
||||
|
||||
Quiet bool
|
||||
Verbose bool
|
||||
Clock string
|
||||
Watch bool
|
||||
DisableLiveReload bool
|
||||
LiveReloadPort int
|
||||
Quiet bool
|
||||
Verbose bool
|
||||
Clock string
|
||||
Watch bool
|
||||
LiveReloadPort int
|
||||
}
|
||||
|
||||
// All non-params config keys for language.
|
||||
@@ -240,9 +241,14 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
disabledKinds := make(map[string]bool)
|
||||
for _, kind := range c.DisableKinds {
|
||||
kind = strings.ToLower(kind)
|
||||
if kind == "taxonomyterm" {
|
||||
if newKind := kinds.IsDeprecatedAndReplacedWith(kind); newKind != "" {
|
||||
logger.Deprecatef(false, "Kind %q used in disableKinds is deprecated, use %q instead.", kind, newKind)
|
||||
// Legacy config.
|
||||
kind = "term"
|
||||
kind = newKind
|
||||
}
|
||||
if kinds.GetKindAny(kind) == "" {
|
||||
logger.Warnf("Unknown kind %q in disableKinds configuration.", kind)
|
||||
continue
|
||||
}
|
||||
disabledKinds[kind] = true
|
||||
}
|
||||
@@ -250,9 +256,17 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
isRssDisabled := disabledKinds["rss"]
|
||||
outputFormats := c.OutputFormats.Config
|
||||
for kind, formats := range c.Outputs {
|
||||
if newKind := kinds.IsDeprecatedAndReplacedWith(kind); newKind != "" {
|
||||
logger.Deprecatef(false, "Kind %q used in outputs configuration is deprecated, use %q instead.", kind, newKind)
|
||||
kind = newKind
|
||||
}
|
||||
if disabledKinds[kind] {
|
||||
continue
|
||||
}
|
||||
if kinds.GetKindAny(kind) == "" {
|
||||
logger.Warnf("Unknown kind %q in outputs configuration.", kind)
|
||||
continue
|
||||
}
|
||||
for _, format := range formats {
|
||||
if isRssDisabled && format == "rss" {
|
||||
// Legacy config.
|
||||
@@ -454,6 +468,9 @@ type RootConfig struct {
|
||||
// Disable the injection of the Hugo generator tag on the home page.
|
||||
DisableHugoGeneratorInject bool
|
||||
|
||||
// Disable live reloading in server mode.
|
||||
DisableLiveReload bool
|
||||
|
||||
// Enable replacement in Pages' Content of Emoji shortcodes with their equivalent Unicode characters.
|
||||
// <docsmeta>{"identifiers": ["Content", "Unicode"] }</docsmeta>
|
||||
EnableEmoji bool
|
||||
@@ -727,7 +744,8 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
cfg := res.Cfg
|
||||
|
||||
all := &Config{}
|
||||
err := decodeConfigFromParams(fs, bcfg, cfg, all, nil)
|
||||
|
||||
err := decodeConfigFromParams(fs, logger, bcfg, cfg, all, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -767,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
|
||||
}
|
||||
}
|
||||
@@ -815,7 +833,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
// Create a copy of the complete config and replace the root keys with the language specific ones.
|
||||
clone := all.cloneForLang()
|
||||
|
||||
if err := decodeConfigFromParams(fs, bcfg, mergedConfig, clone, differentRootKeys); err != nil {
|
||||
if err := decodeConfigFromParams(fs, logger, bcfg, mergedConfig, clone, differentRootKeys); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode config for language %q: %w", k, err)
|
||||
}
|
||||
if err := clone.CompileConfig(logger); err != nil {
|
||||
@@ -869,6 +887,10 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
|
||||
bcfg.PublishDir = all.PublishDir
|
||||
res.BaseConfig = bcfg
|
||||
all.CommonDirs.CacheDir = bcfg.CacheDir
|
||||
for _, l := range langConfigs {
|
||||
l.CommonDirs.CacheDir = bcfg.CacheDir
|
||||
}
|
||||
|
||||
cm := &Configs{
|
||||
Base: all,
|
||||
@@ -883,7 +905,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
func decodeConfigFromParams(fs afero.Fs, bcfg config.BaseConfig, p config.Provider, target *Config, keys []string) error {
|
||||
func decodeConfigFromParams(fs afero.Fs, logger loggers.Logger, bcfg config.BaseConfig, p config.Provider, target *Config, keys []string) error {
|
||||
|
||||
var decoderSetups []decodeWeight
|
||||
|
||||
@@ -896,7 +918,7 @@ func decodeConfigFromParams(fs afero.Fs, bcfg config.BaseConfig, p config.Provid
|
||||
if v, found := allDecoderSetups[key]; found {
|
||||
decoderSetups = append(decoderSetups, v)
|
||||
} else {
|
||||
return fmt.Errorf("unknown config key %q", key)
|
||||
logger.Warnf("Skip unknown config key %q", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -933,11 +955,11 @@ func createDefaultOutputFormats(allFormats output.Formats) map[string][]string {
|
||||
}
|
||||
|
||||
m := map[string][]string{
|
||||
page.KindPage: {htmlOut.Name},
|
||||
page.KindHome: defaultListTypes,
|
||||
page.KindSection: defaultListTypes,
|
||||
page.KindTerm: defaultListTypes,
|
||||
page.KindTaxonomy: defaultListTypes,
|
||||
kinds.KindPage: {htmlOut.Name},
|
||||
kinds.KindHome: defaultListTypes,
|
||||
kinds.KindSection: defaultListTypes,
|
||||
kinds.KindTerm: defaultListTypes,
|
||||
kinds.KindTaxonomy: defaultListTypes,
|
||||
}
|
||||
|
||||
// May be disabled
|
||||
|
||||
@@ -49,10 +49,11 @@ type decodeConfig struct {
|
||||
}
|
||||
|
||||
type decodeWeight struct {
|
||||
key string
|
||||
decode func(decodeWeight, decodeConfig) error
|
||||
getCompiler func(c *Config) configCompiler
|
||||
weight int
|
||||
key string
|
||||
decode func(decodeWeight, decodeConfig) error
|
||||
getCompiler func(c *Config) configCompiler
|
||||
weight int
|
||||
internalOrDeprecated bool // Hide it from the docs.
|
||||
}
|
||||
|
||||
var allDecoderSetups = map[string]decodeWeight{
|
||||
@@ -239,7 +240,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
} else {
|
||||
p.c.Related = related.DefaultConfig
|
||||
if _, found := p.c.Taxonomies["tag"]; found {
|
||||
p.c.Related.Add(related.IndexConfig{Name: "tags", Weight: 80})
|
||||
p.c.Related.Add(related.IndexConfig{Name: "tags", Weight: 80, Type: related.TypeBasic})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -340,6 +341,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
p.c.Author = maps.CleanConfigStringMap(p.p.GetStringMap(d.key))
|
||||
return nil
|
||||
},
|
||||
internalOrDeprecated: true,
|
||||
},
|
||||
"social": {
|
||||
key: "social",
|
||||
@@ -347,6 +349,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
p.c.Social = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
|
||||
return nil
|
||||
},
|
||||
internalOrDeprecated: true,
|
||||
},
|
||||
"uglyurls": {
|
||||
key: "uglyurls",
|
||||
@@ -362,12 +365,14 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
}
|
||||
return nil
|
||||
},
|
||||
internalOrDeprecated: true,
|
||||
},
|
||||
"internal": {
|
||||
key: "internal",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
return mapstructure.WeakDecode(p.p.GetStringMap(d.key), &p.c.Internal)
|
||||
},
|
||||
internalOrDeprecated: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ func (c ConfigLanguage) GetConfigSection(s string) any {
|
||||
return c.config.Permalinks
|
||||
case "minify":
|
||||
return c.config.Minify
|
||||
case "activeModules":
|
||||
case "allModules":
|
||||
return c.m.Modules
|
||||
case "deployment":
|
||||
return c.config.Deployment
|
||||
@@ -228,3 +228,7 @@ func (c ConfigLanguage) PaginatePath() string {
|
||||
func (c ConfigLanguage) StaticDirs() []string {
|
||||
return c.config.staticDirs()
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) EnableEmoji() bool {
|
||||
return c.config.EnableEmoji
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The Hugo Authors. All rights reserved.
|
||||
// Copyright 2023 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.
|
||||
@@ -11,10 +11,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package config
|
||||
package allconfig
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/docshelper"
|
||||
)
|
||||
|
||||
@@ -22,8 +23,11 @@ import (
|
||||
func init() {
|
||||
docsProvider := func() docshelper.DocProvider {
|
||||
|
||||
cfg := New()
|
||||
for _, configRoot := range ConfigRootKeys {
|
||||
cfg := config.New()
|
||||
for configRoot, v := range allDecoderSetups {
|
||||
if v.internalOrDeprecated {
|
||||
continue
|
||||
}
|
||||
cfg.Set(configRoot, make(maps.Params))
|
||||
}
|
||||
lang := maps.Params{
|
||||
@@ -38,7 +42,7 @@ func init() {
|
||||
configHelpers := map[string]any{
|
||||
"mergeStrategy": cfg.Get(""),
|
||||
}
|
||||
return docshelper.DocProvider{"config": configHelpers}
|
||||
return docshelper.DocProvider{"config_helpers": configHelpers}
|
||||
}
|
||||
|
||||
docshelper.AddDocProviderFunc(docsProvider)
|
||||
@@ -79,11 +79,12 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
|
||||
if err := configs.transientErr(); err != nil {
|
||||
return nil, fmt.Errorf("failed to create config from modules config: %w", err)
|
||||
}
|
||||
configs.LoadingInfo.ConfigFiles = append(configs.LoadingInfo.ConfigFiles, l.ModulesConfigFiles...)
|
||||
} else if err := configs.transientErr(); err != nil {
|
||||
return nil, fmt.Errorf("failed to create config: %w", err)
|
||||
}
|
||||
|
||||
configs.Modules = moduleConfig.ActiveModules
|
||||
configs.Modules = moduleConfig.AllModules
|
||||
configs.ModulesClient = modulesClient
|
||||
|
||||
if err := configs.Init(); err != nil {
|
||||
@@ -92,10 +93,10 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
|
||||
|
||||
// This is unfortunate, but these are global settings.
|
||||
tpl.SetSecurityAllowActionJSTmpl(configs.Base.Security.GoTemplates.AllowActionJSTmpl)
|
||||
loggers.InitGlobalLogger(configs.Base.PanicOnWarning)
|
||||
|
||||
loggers.InitGlobalLogger(d.Logger.Level(), configs.Base.PanicOnWarning)
|
||||
|
||||
return configs, nil
|
||||
|
||||
}
|
||||
|
||||
// ConfigSourceDescriptor describes where to find the config (e.g. config.toml etc.).
|
||||
@@ -297,14 +298,20 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error {
|
||||
if nestedKey != "" {
|
||||
owner[nestedKey] = env.Value
|
||||
} else {
|
||||
var val any = env.Value
|
||||
if _, ok := allDecoderSetups[env.Key]; ok {
|
||||
var val any
|
||||
key := strings.ReplaceAll(env.Key, delim, ".")
|
||||
_, ok := allDecoderSetups[key]
|
||||
if ok {
|
||||
// A map.
|
||||
val, err = metadecoders.Default.UnmarshalStringTo(env.Value, map[string]interface{}{})
|
||||
if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]interface{}{}); err == nil {
|
||||
val = v
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
l.cfg.Set(strings.ReplaceAll(env.Key, delim, "."), val)
|
||||
if val == nil {
|
||||
// A string.
|
||||
val = l.envStringToVal(key, env.Value)
|
||||
}
|
||||
l.cfg.Set(key, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,6 +319,19 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *configLoader) envStringToVal(k, v string) any {
|
||||
switch k {
|
||||
case "disablekinds", "disablelanguages":
|
||||
if strings.Contains(v, ",") {
|
||||
return strings.Split(v, ",")
|
||||
} else {
|
||||
return strings.Fields(v)
|
||||
}
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (l *configLoader) loadConfigMain(d ConfigSourceDescriptor) (config.LoadConfigResult, modules.ModulesConfig, error) {
|
||||
var res config.LoadConfigResult
|
||||
|
||||
@@ -450,7 +470,7 @@ func (l *configLoader) loadModules(configs *Configs) (modules.ModulesConfig, *mo
|
||||
ex := hexec.New(conf.Security)
|
||||
|
||||
hook := func(m *modules.ModulesConfig) error {
|
||||
for _, tc := range m.ActiveModules {
|
||||
for _, tc := range m.AllModules {
|
||||
if len(tc.ConfigFilenames()) > 0 {
|
||||
if tc.Watch() {
|
||||
l.ModulesConfigFiles = append(l.ModulesConfigFiles, tc.ConfigFilenames()...)
|
||||
|
||||
@@ -148,7 +148,7 @@ func (b BuildConfig) UseResourceCache(err error) bool {
|
||||
}
|
||||
|
||||
if b.UseResourceCacheWhen == "fallback" {
|
||||
return err == herrors.ErrFeatureNotAvailable
|
||||
return herrors.IsFeatureNotAvailableError(err)
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -333,11 +333,11 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
|
||||
}
|
||||
|
||||
source := c.Source
|
||||
target := c.Target
|
||||
sourceRe, err := regexp.Compile(source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compile cache buster source %q: %w", c.Source, err)
|
||||
}
|
||||
target := c.Target
|
||||
var compileErr error
|
||||
debugl := logger.Logger().WithLevel(logg.LevelDebug).WithField(loggers.FieldNameCmd, "cachebuster")
|
||||
|
||||
@@ -353,23 +353,23 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
|
||||
return nil
|
||||
}
|
||||
groups := m[1:]
|
||||
currentTarget := target
|
||||
// Replace $1, $2 etc. in target.
|
||||
|
||||
for i, g := range groups {
|
||||
target = strings.ReplaceAll(target, fmt.Sprintf("$%d", i+1), g)
|
||||
currentTarget = strings.ReplaceAll(target, fmt.Sprintf("$%d", i+1), g)
|
||||
}
|
||||
targetRe, err := regexp.Compile(target)
|
||||
targetRe, err := regexp.Compile(currentTarget)
|
||||
if err != nil {
|
||||
compileErr = fmt.Errorf("failed to compile cache buster target %q: %w", target, err)
|
||||
compileErr = fmt.Errorf("failed to compile cache buster target %q: %w", currentTarget, err)
|
||||
return nil
|
||||
}
|
||||
return func(s string) bool {
|
||||
match = targetRe.MatchString(s)
|
||||
return func(ss string) bool {
|
||||
match = targetRe.MatchString(ss)
|
||||
matchString := "no match"
|
||||
if match {
|
||||
matchString = "match!"
|
||||
}
|
||||
logger.Debugf("Matching %q with target %q: %s", s, target, matchString)
|
||||
logger.Debugf("Matching %q with target %q: %s", ss, currentTarget, matchString)
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
@@ -164,3 +164,36 @@ func TestBuildConfigCacheBusters(t *testing.T) {
|
||||
c.Assert(m("json"), qt.IsTrue)
|
||||
|
||||
}
|
||||
|
||||
func TestBuildConfigCacheBusterstTailwindSetup(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := New()
|
||||
cfg.Set("build", map[string]interface{}{
|
||||
"cacheBusters": []map[string]string{
|
||||
{
|
||||
"source": "assets/watching/hugo_stats\\.json",
|
||||
"target": "css",
|
||||
},
|
||||
{
|
||||
"source": "(postcss|tailwind)\\.config\\.js",
|
||||
"target": "css",
|
||||
},
|
||||
{
|
||||
"source": "assets/.*\\.(js|ts|jsx|tsx)",
|
||||
"target": "js",
|
||||
},
|
||||
{
|
||||
"source": "assets/.*\\.(.*)$",
|
||||
"target": "$1",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
conf := DecodeBuildConfig(cfg)
|
||||
l := loggers.NewDefault()
|
||||
c.Assert(conf.CompileConfig(l), qt.IsNil)
|
||||
|
||||
m, err := conf.MatchCacheBuster(l, "assets/watching/hugo_stats.json")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(m("css"), qt.IsTrue)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ type AllProvider interface {
|
||||
StaticDirs() []string
|
||||
IgnoredErrors() map[string]bool
|
||||
WorkingDir() string
|
||||
EnableEmoji() bool
|
||||
}
|
||||
|
||||
// Provider provides the configuration settings for Hugo.
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2021 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 security
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/docshelper"
|
||||
)
|
||||
|
||||
func init() {
|
||||
docsProvider := func() docshelper.DocProvider {
|
||||
|
||||
return docshelper.DocProvider{"config": DefaultConfig.ToSecurityMap()}
|
||||
}
|
||||
docshelper.AddDocProviderFunc(docsProvider)
|
||||
}
|
||||
+1
-1
@@ -42,7 +42,7 @@ const (
|
||||
// DefaultArchetypeTemplateTemplate is the template used in 'hugo new site'
|
||||
// and the template we use as a fall back.
|
||||
DefaultArchetypeTemplateTemplate = `---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
title: "{{ replace .File.ContentBaseName "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
+++
|
||||
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||
date = {{ .Date }}
|
||||
draft = true
|
||||
+++
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright 2023 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 skeletons
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
//go:embed all:site/*
|
||||
var siteFs embed.FS
|
||||
|
||||
//go:embed all:theme/*
|
||||
var themeFs embed.FS
|
||||
|
||||
// CreateTheme creates a theme skeleton.
|
||||
func CreateTheme(createpath string, sourceFs afero.Fs) error {
|
||||
if exists, _ := helpers.Exists(createpath, sourceFs); exists {
|
||||
return errors.New(createpath + " already exists")
|
||||
}
|
||||
return copyFiles(createpath, sourceFs, themeFs)
|
||||
}
|
||||
|
||||
// CreateSite creates a site skeleton.
|
||||
func CreateSite(createpath string, sourceFs afero.Fs, force bool, format string) error {
|
||||
format = strings.ToLower(format)
|
||||
if exists, _ := helpers.Exists(createpath, sourceFs); exists {
|
||||
if isDir, _ := helpers.IsDir(createpath, sourceFs); !isDir {
|
||||
return errors.New(createpath + " already exists but not a directory")
|
||||
}
|
||||
|
||||
isEmpty, _ := helpers.IsEmpty(createpath, sourceFs)
|
||||
|
||||
switch {
|
||||
case !isEmpty && !force:
|
||||
return errors.New(createpath + " already exists and is not empty. See --force.")
|
||||
case !isEmpty && force:
|
||||
var all []string
|
||||
fs.WalkDir(siteFs, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if d.IsDir() && path != "." {
|
||||
all = append(all, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
all = append(all, filepath.Join(createpath, "hugo."+format))
|
||||
for _, path := range all {
|
||||
if exists, _ := helpers.Exists(path, sourceFs); exists {
|
||||
return errors.New(path + " already exists")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := newSiteCreateConfig(sourceFs, createpath, format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return copyFiles(createpath, sourceFs, siteFs)
|
||||
}
|
||||
|
||||
func copyFiles(createpath string, sourceFs afero.Fs, skeleton embed.FS) error {
|
||||
return fs.WalkDir(skeleton, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
_, slug, _ := strings.Cut(path, "/")
|
||||
if d.IsDir() {
|
||||
return sourceFs.MkdirAll(filepath.Join(createpath, slug), 0777)
|
||||
} else {
|
||||
if filepath.Base(path) != ".gitkeep" {
|
||||
data, _ := fs.ReadFile(skeleton, path)
|
||||
return helpers.WriteToDisk(filepath.Join(createpath, slug), bytes.NewReader(data), sourceFs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newSiteCreateConfig(fs afero.Fs, createpath string, format string) (err error) {
|
||||
in := map[string]string{
|
||||
"baseURL": "https://example.org/",
|
||||
"title": "My New Hugo Site",
|
||||
"languageCode": "en-us",
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = parser.InterfaceToConfig(in, metadecoders.FormatFromString(format), &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return helpers.WriteToDisk(filepath.Join(createpath, "hugo."+format), &buf, fs)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Theme Name
|
||||
|
||||
## Features
|
||||
|
||||
## Installation
|
||||
|
||||
## Configuration
|
||||
@@ -0,0 +1,5 @@
|
||||
+++
|
||||
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||
date = {{ .Date }}
|
||||
draft = true
|
||||
+++
|
||||
@@ -0,0 +1,22 @@
|
||||
body {
|
||||
color: #222;
|
||||
font-family: sans-serif;
|
||||
line-height: 1.5;
|
||||
margin: 1rem;
|
||||
max-width: 768px;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 1px solid #222;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid #222;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00e;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
console.log('This site was generated by Hugo.');
|
||||
@@ -0,0 +1,9 @@
|
||||
+++
|
||||
title = 'Home'
|
||||
date = 2023-01-01T08:00:00-07:00
|
||||
draft = false
|
||||
+++
|
||||
|
||||
Laborum voluptate pariatur ex culpa magna nostrud est incididunt fugiat
|
||||
pariatur do dolor ipsum enim. Consequat tempor do dolor eu. Non id id anim anim
|
||||
excepteur excepteur pariatur nostrud qui irure ullamco.
|
||||
@@ -0,0 +1,7 @@
|
||||
+++
|
||||
title = 'Posts'
|
||||
date = 2023-01-01T08:30:00-07:00
|
||||
draft = false
|
||||
+++
|
||||
|
||||
Tempor est exercitation ad qui pariatur quis adipisicing aliquip nisi ea consequat ipsum occaecat. Nostrud consequat ullamco laboris fugiat esse esse adipisicing velit laborum ipsum incididunt ut enim. Dolor pariatur nulla quis fugiat dolore excepteur. Aliquip ad quis aliqua enim do consequat.
|
||||
@@ -0,0 +1,10 @@
|
||||
+++
|
||||
title = 'Post 1'
|
||||
date = 2023-01-15T09:00:00-07:00
|
||||
draft = false
|
||||
tags = ['red']
|
||||
+++
|
||||
|
||||
Tempor proident minim aliquip reprehenderit dolor et ad anim Lorem duis sint eiusmod. Labore ut ea duis dolor. Incididunt consectetur proident qui occaecat incididunt do nisi Lorem. Tempor do laborum elit laboris excepteur eiusmod do. Eiusmod nisi excepteur ut amet pariatur adipisicing Lorem.
|
||||
|
||||
Occaecat nulla excepteur dolore excepteur duis eiusmod ullamco officia anim in voluptate ea occaecat officia. Cillum sint esse velit ea officia minim fugiat. Elit ea esse id aliquip pariatur cupidatat id duis minim incididunt ea ea. Anim ut duis sunt nisi. Culpa cillum sit voluptate voluptate eiusmod dolor. Enim nisi Lorem ipsum irure est excepteur voluptate eu in enim nisi. Nostrud ipsum Lorem anim sint labore consequat do.
|
||||
@@ -0,0 +1,10 @@
|
||||
+++
|
||||
title = 'Post 2'
|
||||
date = 2023-02-15T10:00:00-07:00
|
||||
draft = false
|
||||
tags = ['red','green']
|
||||
+++
|
||||
|
||||
Anim eiusmod irure incididunt sint cupidatat. Incididunt irure irure irure nisi ipsum do ut quis fugiat consectetur proident cupidatat incididunt cillum. Dolore voluptate occaecat qui mollit laborum ullamco et. Ipsum laboris officia anim laboris culpa eiusmod ex magna ex cupidatat anim ipsum aute. Mollit aliquip occaecat qui sunt velit ut cupidatat reprehenderit enim sunt laborum. Velit veniam in officia nulla adipisicing ut duis officia.
|
||||
|
||||
Exercitation voluptate irure in irure tempor mollit Lorem nostrud ad officia. Velit id fugiat occaecat do tempor. Sit officia Lorem aliquip eu deserunt consectetur. Aute proident deserunt in nulla aliquip dolore ipsum Lorem ut cupidatat consectetur sit sint laborum. Esse cupidatat sit sint sunt tempor exercitation deserunt. Labore dolor duis laborum est do nisi ut veniam dolor et nostrud nostrud.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,12 @@
|
||||
+++
|
||||
title = 'Post 3'
|
||||
date = 2023-03-15T11:00:00-07:00
|
||||
draft = false
|
||||
tags = ['red','green','blue']
|
||||
+++
|
||||
|
||||
Occaecat aliqua consequat laborum ut ex aute aliqua culpa quis irure esse magna dolore quis. Proident fugiat labore eu laboris officia Lorem enim. Ipsum occaecat cillum ut tempor id sint aliqua incididunt nisi incididunt reprehenderit. Voluptate ad minim sint est aute aliquip esse occaecat tempor officia qui sunt. Aute ex ipsum id ut in est velit est laborum incididunt. Aliqua qui id do esse sunt eiusmod id deserunt eu nostrud aute sit ipsum. Deserunt esse cillum Lorem non magna adipisicing mollit amet consequat.
|
||||
|
||||

|
||||
|
||||
Sit excepteur do velit veniam mollit in nostrud laboris incididunt ea. Amet eu cillum ut reprehenderit culpa aliquip labore laborum amet sit sit duis. Laborum id proident nostrud dolore laborum reprehenderit quis mollit nulla amet veniam officia id id. Aliquip in deserunt qui magna duis qui pariatur officia sunt deserunt.
|
||||
@@ -0,0 +1,23 @@
|
||||
baseURL = 'https://example.org/'
|
||||
languageCode = 'en-us'
|
||||
title = 'My New Hugo Site'
|
||||
|
||||
[[menus.main]]
|
||||
name = 'Home'
|
||||
pageRef = '/'
|
||||
weight = 10
|
||||
|
||||
[[menus.main]]
|
||||
name = 'Posts'
|
||||
pageRef = '/posts'
|
||||
weight = 20
|
||||
|
||||
[[menus.main]]
|
||||
name = 'Tags'
|
||||
pageRef = '/tags'
|
||||
weight = 30
|
||||
|
||||
[module]
|
||||
[module.hugoVersion]
|
||||
extended = false
|
||||
min = "0.116.0"
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ or site.Language.LanguageCode site.Language.Lang }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
|
||||
<head>
|
||||
{{ partial "head.html" . }}
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
{{ partial "header.html" . }}
|
||||
</header>
|
||||
<main>
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
<footer>
|
||||
{{ partial "footer.html" . }}
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
{{ define "main" }}
|
||||
{{ .Content }}
|
||||
{{ range site.RegularPages }}
|
||||
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||
{{ .Summary }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,8 @@
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ .Content }}
|
||||
{{ range .Pages }}
|
||||
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
|
||||
{{ .Summary }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1,10 @@
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
|
||||
{{ $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" }}
|
||||
{{ $dateHuman := .Date | time.Format ":date_long" }}
|
||||
<time datetime="{{ $dateMachine }}">{{ $dateHuman }}</time>
|
||||
|
||||
{{ .Content }}
|
||||
{{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
|
||||
{{ end }}
|
||||
@@ -0,0 +1 @@
|
||||
<p>Copyright {{ now.Year }}. All rights reserved.</p>
|
||||
@@ -0,0 +1,5 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }}</title>
|
||||
{{ partialCached "head/css.html" . }}
|
||||
{{ partialCached "head/js.html" . }}
|
||||
@@ -0,0 +1,9 @@
|
||||
{{- with resources.Get "css/main.css" }}
|
||||
{{- if eq hugo.Environment "development" }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||
{{- else }}
|
||||
{{- with . | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,12 @@
|
||||
{{- with resources.Get "js/main.js" }}
|
||||
{{- if eq hugo.Environment "development" }}
|
||||
{{- with . | js.Build }}
|
||||
<script src="{{ .RelPermalink }}"></script>
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- $opts := dict "minify" true }}
|
||||
{{- with . | js.Build $opts | fingerprint }}
|
||||
<script src="{{ .RelPermalink }}" integrity="{{- .Data.Integrity }}" crossorigin="anonymous"></script>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,2 @@
|
||||
<h1>{{ site.Title }}</h1>
|
||||
{{ partial "menu.html" (dict "menuID" "main" "page" .) }}
|
||||
@@ -0,0 +1,51 @@
|
||||
{{- /*
|
||||
Renders a menu for the given menu ID.
|
||||
|
||||
@context {page} page The current page.
|
||||
@context {string} menuID The menu ID.
|
||||
|
||||
@example: {{ partial "menu.html" (dict "menuID" "main" "page" .) }}
|
||||
*/}}
|
||||
|
||||
{{- $page := .page }}
|
||||
{{- $menuID := .menuID }}
|
||||
|
||||
{{- with index site.Menus $menuID }}
|
||||
<nav>
|
||||
<ul>
|
||||
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
|
||||
</ul>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{- define "partials/inline/menu/walk.html" }}
|
||||
{{- $page := .page }}
|
||||
{{- range .menuEntries }}
|
||||
{{- $attrs := dict "href" .URL }}
|
||||
{{- if $page.IsMenuCurrent .Menu . }}
|
||||
{{- $attrs = merge $attrs (dict "class" "active" "aria-current" "page") }}
|
||||
{{- else if $page.HasMenuCurrent .Menu .}}
|
||||
{{- $attrs = merge $attrs (dict "class" "ancestor" "aria-current" "true") }}
|
||||
{{- end }}
|
||||
{{- $name := .Name }}
|
||||
{{- with .Identifier }}
|
||||
{{- with T . }}
|
||||
{{- $name = . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
<li>
|
||||
<a
|
||||
{{- range $k, $v := $attrs }}
|
||||
{{- with $v }}
|
||||
{{- printf " %s=%q" $k $v | safeHTMLAttr }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
>{{ $name }}</a>
|
||||
{{- with .Children }}
|
||||
<ul>
|
||||
{{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
|
||||
</ul>
|
||||
{{- end }}
|
||||
</li>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,23 @@
|
||||
{{- /*
|
||||
For a given taxonomy, renders a list of terms assigned to the page.
|
||||
|
||||
@context {page} page The current page.
|
||||
@context {string} taxonomy The taxonony.
|
||||
|
||||
@example: {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
|
||||
*/}}
|
||||
|
||||
{{- $page := .page }}
|
||||
{{- $taxonomy := .taxonomy }}
|
||||
|
||||
{{- with $page.GetTerms $taxonomy }}
|
||||
{{- $label := (index . 0).Parent.LinkTitle }}
|
||||
<div>
|
||||
<div>{{ $label }}:</div>
|
||||
<ul>
|
||||
{{- range . }}
|
||||
<li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
</div>
|
||||
{{- end }}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,31 @@
|
||||
name = 'Theme name'
|
||||
license = 'MIT'
|
||||
licenselink = 'https://github.com/owner/repo/LICENSE'
|
||||
description = 'Theme description'
|
||||
|
||||
# The home page of the theme, where the source can be found
|
||||
homepage = 'https://github.com/owner/repo'
|
||||
|
||||
# If you have a running demo of the theme
|
||||
demosite = 'https://owner.github.io/repo'
|
||||
|
||||
# Taxonomy terms
|
||||
tags = ['blog', 'company']
|
||||
features = ['some', 'awesome', 'features']
|
||||
|
||||
# If the theme has multiple authors
|
||||
authors = [
|
||||
{name = 'Name of author', homepage = 'Website of author'},
|
||||
{name = 'Name of author', homepage = 'Website of author'}
|
||||
]
|
||||
|
||||
# If the theme has a single author
|
||||
[author]
|
||||
name = 'Your name'
|
||||
homepage = 'Your website'
|
||||
|
||||
# If porting an existing theme
|
||||
[original]
|
||||
author = 'Name of original author'
|
||||
homepage = 'Website of original author'
|
||||
repo = 'https://github.com/owner/repo'
|
||||
+10
-12
@@ -18,29 +18,27 @@ package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/cloudfront"
|
||||
gcaws "gocloud.dev/aws"
|
||||
)
|
||||
|
||||
// InvalidateCloudFront invalidates the CloudFront cache for distributionID.
|
||||
// It uses the default AWS credentials from the environment.
|
||||
func InvalidateCloudFront(ctx context.Context, distributionID string) error {
|
||||
// SharedConfigEnable enables loading "shared config (~/.aws/config) and
|
||||
// shared credentials (~/.aws/credentials) files".
|
||||
// See https://docs.aws.amazon.com/sdk-for-go/api/aws/session/ for more
|
||||
// details.
|
||||
// This is the same codepath used by Go CDK when creating an s3 URL.
|
||||
// TODO: Update this to a Go CDK helper once available
|
||||
// (https://github.com/google/go-cloud/issues/2003).
|
||||
sess, err := session.NewSessionWithOptions(session.Options{SharedConfigState: session.SharedConfigEnable})
|
||||
// Uses AWS credentials config from the bucket URL.
|
||||
func InvalidateCloudFront(ctx context.Context, target *Target) error {
|
||||
u, err := url.Parse(target.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sess, _, err := gcaws.NewSessionFromURLParams(u.Query())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := &cloudfront.CreateInvalidationInput{
|
||||
DistributionId: aws.String(distributionID),
|
||||
DistributionId: aws.String(target.CloudFrontDistributionID),
|
||||
InvalidationBatch: &cloudfront.InvalidationBatch{
|
||||
CallerReference: aws.String(time.Now().Format("20060102150405")),
|
||||
Paths: &cloudfront.Paths{
|
||||
|
||||
+1
-1
@@ -271,7 +271,7 @@ func (d *Deployer) Deploy(ctx context.Context) error {
|
||||
}
|
||||
} else {
|
||||
d.logger.Println("Invalidating CloudFront CDN...")
|
||||
if err := InvalidateCloudFront(ctx, d.target.CloudFrontDistributionID); err != nil {
|
||||
if err := InvalidateCloudFront(ctx, d.target); err != nil {
|
||||
d.logger.Printf("Failed to invalidate CloudFront CDN: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
Vendored
+7
-2
@@ -33,7 +33,6 @@ import (
|
||||
// There will be normally only one instance of deps in play
|
||||
// at a given time, i.e. one per Site built.
|
||||
type Deps struct {
|
||||
|
||||
// The logger to use.
|
||||
Log loggers.Logger `json:"-"`
|
||||
|
||||
@@ -77,6 +76,9 @@ type Deps struct {
|
||||
// BuildStartListeners will be notified before a build starts.
|
||||
BuildStartListeners *Listeners
|
||||
|
||||
// BuildEndListeners will be notified after a build finishes.
|
||||
BuildEndListeners *Listeners
|
||||
|
||||
// Resources that gets closed when the build is done or the server shuts down.
|
||||
BuildClosers *Closers
|
||||
|
||||
@@ -97,7 +99,6 @@ func (d Deps) Clone(s page.Site, conf config.AllProvider) (*Deps, error) {
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
|
||||
}
|
||||
|
||||
func (d *Deps) SetTempl(t *tpl.TemplateHandlers) {
|
||||
@@ -132,6 +133,10 @@ func (d *Deps) Init() error {
|
||||
d.BuildStartListeners = &Listeners{}
|
||||
}
|
||||
|
||||
if d.BuildEndListeners == nil {
|
||||
d.BuildEndListeners = &Listeners{}
|
||||
}
|
||||
|
||||
if d.BuildClosers == nil {
|
||||
d.BuildClosers = &Closers{}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
||||
### Asking Support Questions
|
||||
### Asking support questions
|
||||
|
||||
We have an active [discussion forum](https://discourse.gohugo.io) where users and developers can ask questions. Please don't use the GitHub issue tracker to ask questions.
|
||||
|
||||
@@ -12,6 +12,18 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.prose-glossary h3 {
|
||||
margin-top: 0;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.prose-glossary h3:first-of-type {
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
.prose-glossary h3 ~ p {
|
||||
margin: 0.5em 0 2em 0;
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
|
||||
+33
-4
@@ -1,5 +1,8 @@
|
||||
.facebook, .twitter, .instagram, .youtube {
|
||||
fill: #BABABA;
|
||||
.facebook,
|
||||
.twitter,
|
||||
.instagram,
|
||||
.youtube {
|
||||
fill: #bababa;
|
||||
}
|
||||
.facebook:hover {
|
||||
fill: #3b5998;
|
||||
@@ -10,10 +13,9 @@
|
||||
}
|
||||
|
||||
.twitter:hover {
|
||||
fill: #BABABA;
|
||||
fill: #bababa;
|
||||
}
|
||||
|
||||
|
||||
.instagram:hover {
|
||||
fill: #e95950;
|
||||
}
|
||||
@@ -21,3 +23,30 @@
|
||||
.youtube:hover {
|
||||
fill: #bb0000;
|
||||
}
|
||||
|
||||
.mstdn {
|
||||
display: inline-block;
|
||||
background-color: #282c37;
|
||||
color: #d9e1e8;
|
||||
text-decoration: none;
|
||||
padding: 4px 10px 4px 30px;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2261.076954mm%22%20height%3D%2265.47831mm%22%20viewBox%3D%220%200%20216.4144%20232.00976%22%3E%3Cpath%20d%3D%22M211.80734%20139.0875c-3.18125%2016.36625-28.4925%2034.2775-57.5625%2037.74875-15.15875%201.80875-30.08375%203.47125-45.99875%202.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125%200%202.53375.15625%204.94625.46875%207.2025%203.38375%2025.68625%2025.47%2027.225%2046.39125%2027.9425%2021.11625.7225%2039.91875-5.20625%2039.91875-5.20625l.8675%2019.09s-14.77%207.93125-41.08125%209.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234%20213.82%201.40609%20165.31125.20859%20116.09125c-.365-14.61375-.14-28.39375-.14-39.91875%200-50.33%2032.97625-65.0825%2032.97625-65.0825C49.67234%203.45375%2078.20359.2425%20107.86484%200h.72875c29.66125.2425%2058.21125%203.45375%2074.8375%2011.09%200%200%2032.975%2014.7525%2032.975%2065.0825%200%200%20.41375%2037.13375-4.59875%2062.915%22%20fill%3D%22%233088d4%22%2F%3E%3Cpath%20d%3D%22M177.50984%2080.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025%200-17.4175%207.5075-17.4175%2022.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375%200-15.74%206.32875-15.74%2018.7975v59.15H38.90484V80.077c0-12.455%203.17125-22.3525%209.54125-29.675%206.56875-7.3225%2015.17125-11.07625%2025.85-11.07625%2012.355%200%2021.71125%204.74875%2027.8975%2014.2475l6.01375%2010.08125%206.015-10.08125c6.185-9.49875%2015.54125-14.2475%2027.8975-14.2475%2010.6775%200%2019.28%203.75375%2025.85%2011.07625%206.36875%207.3225%209.54%2017.22%209.54%2029.675%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E");
|
||||
background-size: 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: top 50% left 8px;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.mstdn:hover {
|
||||
background-color: #484c56;
|
||||
}
|
||||
|
||||
.mstdn > span {
|
||||
color: #9baec8;
|
||||
font-size: 12px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
.mstdn > span:before {
|
||||
content: "@";
|
||||
}
|
||||
|
||||
+3
-2
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 9.3 KiB |
Generated
+6
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
Generated
+6
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
@@ -4926,6 +4926,16 @@ pre {
|
||||
background: #0594CB; /* WebKit/Blink Browsers */
|
||||
color: white;
|
||||
}
|
||||
.prose-glossary h3 {
|
||||
margin-top: 0;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
.prose-glossary h3:first-of-type {
|
||||
margin-top: 3em;
|
||||
}
|
||||
.prose-glossary h3 ~ p {
|
||||
margin: 0.5em 0 2em 0;
|
||||
}
|
||||
body {
|
||||
|
||||
line-height: 1.45;
|
||||
@@ -5103,8 +5113,11 @@ code, .code, pre code, .highlight pre {
|
||||
-webkit-transition: opacity .15s ease-in;
|
||||
transition: opacity .15s ease-in;
|
||||
}
|
||||
.facebook, .twitter, .instagram, .youtube {
|
||||
fill: #BABABA;
|
||||
.facebook,
|
||||
.twitter,
|
||||
.instagram,
|
||||
.youtube {
|
||||
fill: #bababa;
|
||||
}
|
||||
.facebook:hover {
|
||||
fill: #3b5998;
|
||||
@@ -5113,7 +5126,7 @@ code, .code, pre code, .highlight pre {
|
||||
fill: #55acee;
|
||||
}
|
||||
.twitter:hover {
|
||||
fill: #BABABA;
|
||||
fill: #bababa;
|
||||
}
|
||||
.instagram:hover {
|
||||
fill: #e95950;
|
||||
@@ -5121,6 +5134,32 @@ code, .code, pre code, .highlight pre {
|
||||
.youtube:hover {
|
||||
fill: #bb0000;
|
||||
}
|
||||
.mstdn {
|
||||
display: inline-block;
|
||||
background-color: #282c37;
|
||||
color: #d9e1e8;
|
||||
text-decoration: none;
|
||||
padding: 4px 10px 4px 30px;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2261.076954mm%22%20height%3D%2265.47831mm%22%20viewBox%3D%220%200%20216.4144%20232.00976%22%3E%3Cpath%20d%3D%22M211.80734%20139.0875c-3.18125%2016.36625-28.4925%2034.2775-57.5625%2037.74875-15.15875%201.80875-30.08375%203.47125-45.99875%202.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125%200%202.53375.15625%204.94625.46875%207.2025%203.38375%2025.68625%2025.47%2027.225%2046.39125%2027.9425%2021.11625.7225%2039.91875-5.20625%2039.91875-5.20625l.8675%2019.09s-14.77%207.93125-41.08125%209.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234%20213.82%201.40609%20165.31125.20859%20116.09125c-.365-14.61375-.14-28.39375-.14-39.91875%200-50.33%2032.97625-65.0825%2032.97625-65.0825C49.67234%203.45375%2078.20359.2425%20107.86484%200h.72875c29.66125.2425%2058.21125%203.45375%2074.8375%2011.09%200%200%2032.975%2014.7525%2032.975%2065.0825%200%200%20.41375%2037.13375-4.59875%2062.915%22%20fill%3D%22%233088d4%22%2F%3E%3Cpath%20d%3D%22M177.50984%2080.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025%200-17.4175%207.5075-17.4175%2022.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375%200-15.74%206.32875-15.74%2018.7975v59.15H38.90484V80.077c0-12.455%203.17125-22.3525%209.54125-29.675%206.56875-7.3225%2015.17125-11.07625%2025.85-11.07625%2012.355%200%2021.71125%204.74875%2027.8975%2014.2475l6.01375%2010.08125%206.015-10.08125c6.185-9.49875%2015.54125-14.2475%2027.8975-14.2475%2010.6775%200%2019.28%203.75375%2025.85%2011.07625%206.36875%207.3225%209.54%2017.22%209.54%2029.675%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E");
|
||||
background-size: 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: top 50% left 8px;
|
||||
-webkit-transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.mstdn:hover {
|
||||
background-color: #484c56;
|
||||
}
|
||||
.mstdn > span {
|
||||
color: #9baec8;
|
||||
font-size: 12px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
.mstdn > span:before {
|
||||
content: "@";
|
||||
}
|
||||
@media (min-width: 75em) {
|
||||
|
||||
[data-scrolldir="down"] .sticky {
|
||||
|
||||
+11
-10
@@ -3,19 +3,20 @@
|
||||
link = "https://www.linode.com/"
|
||||
logo = "images/sponsors/linode-logo.svg"
|
||||
utm_campaign = "hugosponsor"
|
||||
bgcolor = "#ffffff"
|
||||
|
||||
[[banners]]
|
||||
name = "CloudCannon"
|
||||
link = "https://cloudcannon.com/hugo-cms/"
|
||||
logo = "/images/sponsors/cloudcannon-white.svg"
|
||||
utm_campaign = "HugoSponsorship"
|
||||
utm_source = "sponsor"
|
||||
utm_content = "gohugo"
|
||||
bgcolor = "#034AD8"
|
||||
|
||||
[[banners]]
|
||||
name = "Your Company?"
|
||||
link = "https://bep.is/en/hugo-sponsor-2023-01/"
|
||||
logo = "/images/sponsors/your-company.svg"
|
||||
utm_campaign = "hugosponsor"
|
||||
show_on_hover = true
|
||||
bgcolor = "#004887"
|
||||
|
||||
[[banners]]
|
||||
name = "Your Company?"
|
||||
link = "https://bep.is/en/hugo-sponsor-2023-01/"
|
||||
logo = "/images/sponsors/your-company.svg"
|
||||
utm_campaign = "hugosponsor"
|
||||
show_on_hover = true
|
||||
bgcolor = "#004887"
|
||||
bgcolor = "#4e4f4f"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<img src="{{ . }}" alt="Featured Image for {{ $.Title }}" class="mw-100">
|
||||
{{ end }}
|
||||
|
||||
<div class="prose" id="prose">
|
||||
<div class="prose prose-{{ .Type }}" id="prose">
|
||||
{{- partial "docs/functions-signature.html" . -}}
|
||||
{{ .Content }}
|
||||
</div>
|
||||
|
||||
Generated
+3
-3
@@ -23,7 +23,7 @@
|
||||
class="{{ $classes_box }} o-100"
|
||||
style="background-color: {{ .bgcolor }};">
|
||||
{{ $query_params := .query_params | default "" }}
|
||||
{{ $url := printf "%s?%s%s" .link $query_params (querify "utm_source" $utmSource "utm_medium" "banner" "utm_campaign" (.utm_campaign | default "hugosponsor")) | safeURL }}
|
||||
{{ $url := printf "%s?%s%s" .link $query_params (querify "utm_source" (.utm_source | default $utmSource ) "utm_medium" "banner" "utm_campaign" (.utm_campaign | default "hugosponsor") "utm_content" (.utm_content | default "gohugoio")) | safeURL }}
|
||||
{{ $logo := resources.Get .logo }}
|
||||
{{ if hugo.IsProduction }}
|
||||
{{ $gtagID := printf "Sponsor %s %s" .name $gtag | title }}
|
||||
@@ -34,7 +34,7 @@
|
||||
show-on-hover
|
||||
{{ end }}"
|
||||
style="">
|
||||
{{ $logo.Content | safeHTML }}
|
||||
{{ with $logo }}{{ .Content | safeHTML }}{{ end }}
|
||||
</a>
|
||||
{{ else }}
|
||||
<a
|
||||
@@ -42,7 +42,7 @@
|
||||
class="w-100 grow pa3{{ if .show_on_hover }}
|
||||
show-on-hover
|
||||
{{ end }}">
|
||||
{{ $logo.Content | safeHTML }}
|
||||
{{ with $logo }}{{ .Content | safeHTML }}{{ end }}
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -30,9 +30,9 @@
|
||||
{{- partial "site-search.html" . -}}
|
||||
</div>
|
||||
|
||||
<span class="absolute mt1 mt2-l pr3 right-0 top-0">
|
||||
<div class="absolute mt1 mt2-l pr3 right-0 top-0 flex items-start">
|
||||
{{- partialCached "social-follow.html" . -}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
+6
-4
@@ -1,7 +1,9 @@
|
||||
<!-- TODO: Add follow intents where available TODO: Revisit color and hover color -->
|
||||
{{ with .Site.Social.twitter }}
|
||||
{{/*
|
||||
Disable Twitter for now as we lost access to the account.
|
||||
with .Site.Social.twitter }}
|
||||
<a href="https://twitter.com/intent/follow?screen_name={{ . }}" title="Follow on Twitter" class="link-transition twitter link dib z-999 pt3 pt0-l mr2">
|
||||
{{ partial "svg/twitter.svg" (dict "size" "32px") }}
|
||||
</a>
|
||||
{{ end }}
|
||||
<a class="github-button needs-js link primary-color-dark" href="https://github.com/gohugoio/hugo" data-size="large" data-show-count="false" aria-label="Star gohugoio/hugo on GitHub">Star</a>
|
||||
{{ end */}}
|
||||
<a rel="me" class="mstdn mr3" href="https://fosstodon.org/@gohugoio" target="_blank">gohugoio</a>
|
||||
<a class="github-button needs-js link primary-color-dark" href="https://github.com/gohugoio/hugo" data-size="large" data-show-count="false" aria-label="Star gohugoio/hugo on GitHub">Star</a>
|
||||
|
||||
@@ -1 +1 @@
|
||||
# github.com/gohugoio/gohugoioTheme v0.0.0-20230527124826-78bc315d7b8a
|
||||
# github.com/gohugoio/gohugoioTheme v0.0.0-20230927165800-342e2c850f18
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
---
|
||||
title: {{ replace .Name "-" " " | title }}
|
||||
description: ""
|
||||
signature: []
|
||||
title: {{ replace .File.ContentBaseName "-" " " | title }}
|
||||
description:
|
||||
categories: [functions]
|
||||
keywords: []
|
||||
menu:
|
||||
docs:
|
||||
parent: functions
|
||||
relatedfuncs: []
|
||||
function:
|
||||
aliases: []
|
||||
returnType:
|
||||
signatures: []
|
||||
relatedFunctions: []
|
||||
---
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
description: ""
|
||||
title: {{ replace .File.ContentBaseName "-" " " | title }}
|
||||
description:
|
||||
date: {{ .Date }}
|
||||
---
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
|
||||
title: {{ replace .Name "-" " " | title }}
|
||||
title: {{ replace .File.ContentBaseName "-" " " | title }}
|
||||
date: {{ now.Format "2006-01-02" }}
|
||||
|
||||
description: "A short description of this page."
|
||||
description: A short description of this page.
|
||||
|
||||
# The URL to the site on the internet.
|
||||
siteURL: https://gohugo.io/
|
||||
@@ -34,4 +34,3 @@ bio.md
|
||||
|
||||
featured.png
|
||||
: A reasonably sized screenshot of your website. It can be named anything, but the name must start with "featured". The sample image is `1500x750` (2:1 aspect ratio).
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
url = "/installation/"
|
||||
|
||||
[[docs]]
|
||||
name = "Getting Started"
|
||||
name = "Getting started"
|
||||
weight = 30
|
||||
identifier = "getting-started"
|
||||
url = "/getting-started/"
|
||||
@@ -26,7 +26,7 @@
|
||||
# Core menus
|
||||
|
||||
[[docs]]
|
||||
name = "Content Management"
|
||||
name = "Content management"
|
||||
weight = 50
|
||||
identifier = "content-management"
|
||||
post = "expanded"
|
||||
@@ -53,7 +53,7 @@
|
||||
[[docs]]
|
||||
name = "Hugo Pipes"
|
||||
weight = 90
|
||||
identifier = "pipes"
|
||||
identifier = "hugo-pipes"
|
||||
url = "/hugo-pipes/"
|
||||
|
||||
[[docs]]
|
||||
@@ -72,13 +72,13 @@
|
||||
url = "/troubleshooting/"
|
||||
|
||||
[[docs]]
|
||||
name = "Tools"
|
||||
name = "Developer tools"
|
||||
weight = 120
|
||||
identifier = "tools"
|
||||
identifier = "developer-tools"
|
||||
url = "/tools/"
|
||||
|
||||
[[docs]]
|
||||
name = "Hosting & Deployment"
|
||||
name = "Hosting and deployment"
|
||||
weight = 130
|
||||
identifier = "hosting-and-deployment"
|
||||
url = "/hosting-and-deployment/"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user