Compare commits

...

427 Commits

Author SHA1 Message Date
hugoreleaser d8aba18e05 releaser: Prepare repository for 0.105.0-DEV
[ci skip]
2022-09-23 14:44:31 +00:00
hugoreleaser c744dbd6ed releaser: Bump versions for release of 0.104.0
[ci skip]
2022-09-23 14:32:56 +00:00
Bjørn Erik Pedersen 5c41653364 Consolidate the glob case logic
Looking at the code as a whole, we ended up with a little to much "buttons". It turns out that doing case insensitive matching (lower both pattern and strings to match) performs just fine. Or at least, it
gives the penalty to the people who uses mixed case filenames.

```
GetGlob/Default_cache-10                          10.6ns ± 2%    10.6ns ± 1%   ~     (p=0.657 n=4+4)
GetGlob/Filenames_cache,_lowercase_searchs-10     10.6ns ± 2%    10.6ns ± 0%   ~     (p=1.000 n=4+4)
GetGlob/Filenames_cache,_mixed_case_searchs-10    29.7ns ± 1%    29.6ns ± 1%   ~     (p=0.886 n=4+4)
GetGlob/GetGlob-10                                13.7ns ± 1%    13.7ns ± 0%   ~     (p=0.429 n=4+4)

name                                            old alloc/op   new alloc/op   delta
GetGlob/Default_cache-10                           0.00B          0.00B        ~     (all equal)
GetGlob/Filenames_cache,_lowercase_searchs-10      0.00B          0.00B        ~     (all equal)
GetGlob/Filenames_cache,_mixed_case_searchs-10     5.00B ± 0%     5.00B ± 0%   ~     (all equal)
GetGlob/GetGlob-10                                 0.00B          0.00B        ~     (all equal)

name                                            old allocs/op  new allocs/op  delta
GetGlob/Default_cache-10                            0.00           0.00        ~     (all equal)
GetGlob/Filenames_cache,_lowercase_searchs-10       0.00           0.00        ~     (all equal)
GetGlob/Filenames_cache,_mixed_case_searchs-10      1.00 ± 0%      1.00 ± 0%   ~     (all equal)
GetGlob/GetGlob-10
```
2022-09-23 16:19:21 +02:00
satotake 281554ee97 hugofs: Fix glob case-sensitivity bug
On Linux, `hugofs.Glob` does not hit any directories which includes
uppercase letters. (This does not happen on macOS.)

Since `resources.GetMatch/Match` uses `Glob`,

```
{{ resources.GetMatch "Foo/bar.css" }}
```

this does not match `assets/Foo/bar.css` .

On the other hand, you can get it with

```
{{ resources.Get "Foo/bar.css" }}
```
2022-09-23 13:12:57 +02:00
Bjørn Erik Pedersen f3560aa0e1 server: Fix 404 redirects on Windows
Fixes #10314
2022-09-23 13:07:56 +02:00
dependabot[bot] edf9038a92 build(deps): bump github.com/evanw/esbuild from 0.15.8 to 0.15.9
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.15.8 to 0.15.9.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.15.8...v0.15.9)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-23 10:56:45 +02:00
dependabot[bot] 78f49b4c0d build(deps): bump github.com/yuin/goldmark from 1.4.14 to 1.4.15
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.4.14 to 1.4.15.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.4.14...v1.4.15)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-22 19:25:55 +02:00
dependabot[bot] fa4b77e7ed build(deps): bump github.com/getkin/kin-openapi from 0.100.0 to 0.103.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.100.0 to 0.103.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.100.0...v0.103.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-22 19:05:44 +02:00
Bjørn Erik Pedersen 8377c3cea1 docs: Regen docs helper 2022-09-22 18:55:00 +02:00
dependabot[bot] 4d909d4768 build(deps): bump github.com/alecthomas/chroma/v2 from 2.2.0 to 2.3.0
Bumps [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Changelog](https://github.com/alecthomas/chroma/blob/master/.goreleaser.yml)
- [Commits](https://github.com/alecthomas/chroma/compare/v2.2.0...v2.3.0)

---
updated-dependencies:
- dependency-name: github.com/alecthomas/chroma/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-22 18:54:27 +02:00
dependabot[bot] 4eb6d97404 build(deps): bump github.com/evanw/esbuild from 0.15.7 to 0.15.8
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.15.7 to 0.15.8.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.15.7...v0.15.8)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-22 18:53:12 +02:00
Bjørn Erik Pedersen 4f9cb4f344 docs: Regenerate CLI docs 2022-09-22 17:57:13 +02:00
Bjørn Erik Pedersen 0171fb2010 Run go mod tidy 2022-09-22 17:42:48 +02:00
Bjørn Erik Pedersen a4028112e3 resources/images: Add $image.Colors
Which returns the most dominant colors of an image using a simple histogram method.

Fixes #10307
2022-09-22 15:57:35 +02:00
Bjørn Erik Pedersen 08f0984f91 commands: Skip flaky test on CI 2022-09-22 11:24:42 +02:00
Mathieu Parent 86653fa38e config/security: Allow proxy variables in subcommands
In particular for go get
2022-09-19 12:37:35 +02:00
hugoreleaser c46d104985 releaser: Prepare repository for 0.104.0-DEV
[ci skip]
2022-09-18 13:31:21 +00:00
hugoreleaser b665f1e8f1 releaser: Bump versions for release of 0.103.1
[ci skip]
2022-09-18 13:19:01 +00:00
Bjørn Erik Pedersen 6be6752c8a server: Fix redirects when file path contains bytes > 0x80
Fixes #10287
2022-09-18 15:16:11 +02:00
Bjørn Erik Pedersen 8e9dce1092 Merge branch 'release-0.103.0' 2022-09-15 18:47:37 +02:00
hugoreleaser 00b71668b2 releaser: Prepare repository for 0.104.0-DEV
[ci skip]
2022-09-15 16:35:38 +00:00
hugoreleaser beebf2afb0 releaser: Bump versions for release of 0.103.0
[ci skip]
2022-09-15 16:23:56 +00:00
Bjørn Erik Pedersen 3f0b40f674 Use standard GOOS/GOARCH values in release archives
But create aliases with old filenames for the 2 most downloaded archives (to avoid Netlify etc. breaking).

Fixes #10073
2022-09-15 18:22:35 +02:00
Bjørn Erik Pedersen 0bd79d30c1 Use standard GOOS/GOARCH values in release archives
But create aliases with old filenames for the 2 most downloaded archives (to avoid Netlify etc. breaking).

Fixes #10073
2022-09-15 12:26:22 +02:00
Bjørn Erik Pedersen 8e77bcc930 Filter out any duplicate files to post process
Updates #10269
2022-09-14 19:42:55 +02:00
Bjørn Erik Pedersen 74daca6b30 Support PostProcess for all file types
Not just HTML.

Fixes #10269
2022-09-14 19:09:20 +02:00
dependabot[bot] 1fd4c562af build(deps): bump github.com/gobuffalo/flect from 0.2.5 to 0.3.0
Bumps [github.com/gobuffalo/flect](https://github.com/gobuffalo/flect) from 0.2.5 to 0.3.0.
- [Release notes](https://github.com/gobuffalo/flect/releases)
- [Commits](https://github.com/gobuffalo/flect/compare/v0.2.5...v0.3.0)

---
updated-dependencies:
- dependency-name: github.com/gobuffalo/flect
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 18:12:47 +02:00
Bjørn Erik Pedersen a5cda5ca4d server: Add 404 support 2022-09-14 14:25:33 +02:00
dependabot[bot] 5e2b28d6e6 build(deps): bump github.com/getkin/kin-openapi from 0.98.0 to 0.100.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.98.0 to 0.100.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.98.0...v0.100.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 11:21:14 +02:00
dependabot[bot] f2019f0a23 build(deps): bump github.com/evanw/esbuild from 0.15.5 to 0.15.7
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.15.5 to 0.15.7.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.15.5...v0.15.7)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 10:58:32 +02:00
dependabot[bot] 475638fe02 build(deps): bump github.com/yuin/goldmark from 1.4.13 to 1.4.14
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.4.13 to 1.4.14.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.4.13...v1.4.14)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 10:58:12 +02:00
Bjørn Erik Pedersen 203cc54578 Feat/sponsors in readme (#10273)
* Update README.md

* Update README.md
2022-09-14 10:20:57 +02:00
Bjørn Erik Pedersen af23cdca9c Merge commit '90ad8045056167004d27857a95542936657b8a16' 2022-09-13 20:34:24 +02:00
Bjørn Erik Pedersen 90ad804505 Squashed 'docs/' changes from e5aa641a6..392668f4f
392668f4f Update theme
65d82c845 fix versions in GitHub Pages docs (#1815)
4e078a306 Create hosting-on-azure-static-web-apps.md (#1738)
e24e25052 fix requirement typo (#1814)
0790eb173 fix broken link (#1813)
f4a1b38c7 📄 Add more clarity on merging of config files (#1493)
2e82efff0 Install on Windows: Correct + augment (#1520)
4bffd076e Update frontends to add CloudCannon's CMS (#1509)
17eea3133 Update index.md to add resource (#1508)
5a5ac1d2f Add documentation for babel sourceMap (#1492)
899b7117c Update menu-templates.md
284dc4266 Include flexible translation in i18n.md
f03421274 docs: Escaping Hugo/GO template code
4f0755683 Improve the documentation of imageConfig and the image resource
89aa723cc Clarify leaf bundle explanation and related FAQ
0c6b32bb9 Update starter-kits.md
a68151b1b Update starter-kits.md
91b145384 Update starter-kits.md
c8104b422 Update hosting-on-21yunbox.md
51ee7603b Update hosting-on-21yunbox.md
d88314499 docs(en): add hosting on 21YunBox
aab04f269 Update shortcode-templates.md to correct an error.
ed48563aa Misc improvements
87dd24e1d Fix merge failure
0bcc6dca8 js.Build: Update docs to not allow boolean inputs for sourceMap
e50a28fbc js.Build: Add SourceMap flag into js.Build opts which can turn on sourcemap
9695093a1 Fix Arch Linux installation command
3de773d7a Please remove hugo-elasticsearch plugin.
6510f0a5a release: Add some more ignore expressions to release notes config
dc90b7517 typoe > typo (!)
3427c7436 Add hugoreleaser config
5a1f2d0dd Improves formatting of resources, assets sections (#1804)
03ba56fdd Remove Flesland Flis from Showcases
9f61dac7a Update slice.md
533e4e0cd Update theme
85e50325c Simplify writing
9b30e81b9 Typo fix and remove passive form
8974b6c53 dynamic-menu-configuration
1c5467329 netlify: Hugo 0.102.3
610a937b0 Remove Over from Showcase
99f5585bc  netlify: Hugo v0.102.2
9f230ac1f netlify: Hugo v0.102.1
a6fc3f864 netlify: Bump to Hugo v0.102.0
3e9bc1a62 Merge branch 'tempv0.102.0'
c08d6d898 Update en/templates/404.md with Firebase Hosting (#1796)
322b75f40 Update configuration.md
2fa6f0b94 404 template example: remove slash relURL arg
1195f168a Remove broken link (#1767)
e0838e574 Update RenderString.md
bee6adf71 Update page-resources.md
24e142f22 Remove duplicate word from cascade description
879fc3983 docs: Update the description of PostCSS config
2ffe539e3 fix: Use `=` instead of `:=` for variable reassignments (#1771)
7496b8f87 update 404 error for digitalocean docs
c85caca4a Merge commit 'bdf935d66c1f02dfc942a30e9fc00519bba3aacb'
c3888b63a docs: Regen docshelper
8a5942555 Merge commit '475f87f685439de0f907a9ffc29bfd1361eb1c59'
282007217 common: Add hugo.GoVersion
00b4b46da resources/page: Add :slugorfilename attribute

git-subtree-dir: docs
git-subtree-split: 392668f4f4
2022-09-13 20:34:24 +02:00
satotake ab5ce59894 Fix usage description 2022-09-08 15:35:11 +02:00
satotake 7d40da876c Add --force to hugo new
Closes #9243
2022-09-08 15:35:11 +02:00
Jenny Rakoczy 02c89a446d scss: Handle single-file sourcemaps correctly
Simple sites may only have one css file. Update the replace directive to
correctly match single-file sourcemaps and multi-file sourcemaps.

Verified locally with and without SASS and CSS imports.

Fixes #8174
2022-09-06 10:52:28 +02:00
Bjørn Erik Pedersen 06c3ac6741 release: Bump Hugoreleaser version 2022-09-03 11:38:03 +02:00
Alex 5e03de0ddb Update stale.yml
Signed-off-by: sashashura <93376818+sashashura@users.noreply.github.com>
2022-09-02 20:13:02 +02:00
hugoreleaser bef31b58aa releaser: Prepare repository for 0.103.0-DEV
[ci skip]
2022-09-01 10:34:57 +00:00
hugoreleaser b76146b129 releaser: Bump versions for release of 0.102.3
[ci skip]
2022-09-01 10:16:19 +00:00
Bjørn Erik Pedersen 8e5044d7f5 Fix shortcode parser regression with quoted param values
This issue was introduced in `v0.102.0`.

In 223bf28004 we removed the byte source from the parsed page result, which
meant we had to preserve exact positioning for all elements. This introduced some new `TypeIgnore` tokens
which we, wrongly, assumed didn't matter where we put in the result slice (they should be ignored anyway).

But it seems that this broke the logic where we determine if it's positional or named params in the case
where the paramater value contains escaped quoutes.

This commit makes sure that these ignore tokens (the back slashes) are never sent back to the client, which is how it was before `v0.102.0`.

This commit also fixes some lost error information in that same commit.

Fixes #10236
2022-09-01 12:13:23 +02:00
Joe Mooring 5046a6c7ca deps: Update github.com/tdewolff/minify/v2 v2.12.0 => v2.12.1
Fixes #10230
2022-08-31 09:02:50 +02:00
Anthony Fok 160a067c81 snap: Use "snapcraftctl set-grade"
This, together with 'snapcraftctl set-version", negates the need to
modify snap/snapcraft.yaml upon each release, so the corresponding code
is removed from releaser/releaser.go.

Also, vendorInfo is extended to include the snap version number.

See #10225
2022-08-30 05:23:18 -06:00
Anthony Fok e0ba1a805a snap: Use "snapcraftctl set-version"
This allows the use of "git describe --tags" to recognize lightweight
version tags, and the removal of initial "v" in the hugo snap version.

See #10225
2022-08-29 23:14:22 -06:00
Anthony Fok 7b49c56a6a snap: Make external dependencies actually work
Git:
 - Set GIT_EXEC_PATH and include usr/lib/git-core so that git can find the
   git-remote-https helper (needed by e.g. "go mod download").

Go:
 - Put Go in its own snap part, and use stage-snaps instead of build-snaps
   so that it is included in the final snap.
 - Set GOCACHE to a writable directory. (In a previous commit,
   HOME is set to $SNAP_REAL_HOME which is unwritable.)

Hugo:
 - Patch config/security/securityConfig.go "[security.exec] osEnv" whitelist
   (during snap build) so that external dependencies can use the required
   environment variables to run properly from within the snap.

Asciidoctor:
 - Replace shebang line in asciidoctor so it can find the ruby executable.
 - Set RUBYLIB so that Ruby can find its libraries.
   Caveat/TODO: The Ruby version is hardcoded in our custom RUBYLIB.

Embedded Dart Sass:
 - Download from GitHub and install it, for amd64 and arm64 only.

Node.js:
 - Remove my incomplete include list so that npx is actually installed.
 - Set npm_config_{cache,init_module,userconfig} to writable locations.

Pandoc:
 - Set pandoc_datadir so that Pandoc can find its data files.

rst2html:
 - Install python3-docutils package for rst2html, rst2html5, etc.
 - Set PYTHONHOME so that Python can find its libraries.

Note that asciidoctor, pandoc and rst2html are not in Hugo’s default
"[security.exec] allow" whitelist, and the snap package does not change
that default, so they still needed to be whitelisted manually in
config.toml if necessary by the end user.

Special thanks to Joe Mooring (@jmooring) for meticulously diagnosing
the issue and providing a comprehensive test repository at
https://github.com/jmooring/hugo-snap-test without which I would not
have been able to understand and resolve the issue.

Fixes #9078
2022-08-29 12:24:58 -06:00
Bjørn Erik Pedersen dffca57883 release: Add the releaser commits to the ignore list 2022-08-29 16:29:45 +02:00
Bjørn Erik Pedersen 79932e7225 release: Fix the Deb archives
Fixes #10220
2022-08-29 14:56:15 +02:00
hugoreleaser 9eb9b70a29 releaser: Prepare repository for 0.103.0-DEV
[ci skip]
2022-08-28 16:41:06 +00:00
hugoreleaser 0ff4a9326f releaser: Bump versions for release of 0.102.0
[ci skip]
2022-08-28 16:29:34 +00:00
Bjørn Erik Pedersen 45e1084ff2 Add linux/arm64 extended to release setup
Fixes #8257
2022-08-28 18:23:01 +02:00
Bjørn Erik Pedersen c98348416c license: Add copyright info
Closes #10218
2022-08-28 12:55:47 +02:00
dependabot[bot] 2de393c791 build(deps): bump go.uber.org/atomic from 1.9.0 to 1.10.0
Bumps [go.uber.org/atomic](https://github.com/uber-go/atomic) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/uber-go/atomic/releases)
- [Changelog](https://github.com/uber-go/atomic/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uber-go/atomic/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: go.uber.org/atomic
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-28 12:02:17 +02:00
dependabot[bot] 7efb35680f build(deps): bump github.com/kyokomi/emoji/v2 from 2.2.9 to 2.2.10
Bumps [github.com/kyokomi/emoji/v2](https://github.com/kyokomi/emoji) from 2.2.9 to 2.2.10.
- [Release notes](https://github.com/kyokomi/emoji/releases)
- [Commits](https://github.com/kyokomi/emoji/compare/v2.2.9...v2.2.10)

---
updated-dependencies:
- dependency-name: github.com/kyokomi/emoji/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-26 18:50:51 +02:00
dependabot[bot] ddbcc67122 build(deps): bump github.com/getkin/kin-openapi from 0.97.0 to 0.98.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.97.0 to 0.98.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.97.0...v0.98.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-26 18:48:56 +02:00
Bjørn Erik Pedersen fd75f129b2 deps: Update github.com/pelletier/go-toml/v2 v2.0.2 => v2.0.4
Closes #10210
2022-08-26 18:30:46 +02:00
dependabot[bot] 14878ca0a0 build(deps): bump github.com/spf13/afero from 1.8.2 to 1.9.2
Bumps [github.com/spf13/afero](https://github.com/spf13/afero) from 1.8.2 to 1.9.2.
- [Release notes](https://github.com/spf13/afero/releases)
- [Commits](https://github.com/spf13/afero/compare/v1.8.2...v1.9.2)

---
updated-dependencies:
- dependency-name: github.com/spf13/afero
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-26 12:01:51 +02:00
dependabot[bot] e88873b803 build(deps): bump github.com/tdewolff/parse/v2 from 2.6.1 to 2.6.2
Bumps [github.com/tdewolff/parse/v2](https://github.com/tdewolff/parse) from 2.6.1 to 2.6.2.
- [Release notes](https://github.com/tdewolff/parse/releases)
- [Commits](https://github.com/tdewolff/parse/compare/v2.6.1...v2.6.2)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/parse/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-26 12:01:19 +02:00
dependabot[bot] 4219993b0c build(deps): bump github.com/mattn/go-isatty from 0.0.14 to 0.0.16
Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.14 to 0.0.16.
- [Release notes](https://github.com/mattn/go-isatty/releases)
- [Commits](https://github.com/mattn/go-isatty/compare/v0.0.14...v0.0.16)

---
updated-dependencies:
- dependency-name: github.com/mattn/go-isatty
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-25 12:28:50 +02:00
dependabot[bot] 988e1417a0 build(deps): bump github.com/rogpeppe/go-internal from 1.8.1 to 1.9.0
Bumps [github.com/rogpeppe/go-internal](https://github.com/rogpeppe/go-internal) from 1.8.1 to 1.9.0.
- [Release notes](https://github.com/rogpeppe/go-internal/releases)
- [Commits](https://github.com/rogpeppe/go-internal/compare/v1.8.1...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/rogpeppe/go-internal
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-23 13:36:47 +02:00
dependabot[bot] 42529882ce build(deps): bump github.com/yuin/goldmark from 1.4.12 to 1.4.13
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.4.12 to 1.4.13.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.4.12...v1.4.13)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-23 13:34:24 +02:00
dependabot[bot] 45f1b1cfc5 build(deps): bump github.com/spf13/cobra from 1.4.0 to 1.5.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-23 06:53:38 +02:00
dependabot[bot] 369bdf22f8 build(deps): bump github.com/tdewolff/minify/v2 from 2.11.10 to 2.12.0
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.11.10 to 2.12.0.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.11.10...v2.12.0)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-23 00:00:14 +02:00
dependabot[bot] d1b03a093f build(deps): bump github.com/evanw/esbuild from 0.14.43 to 0.15.5
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.43 to 0.15.5.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.43...v0.15.5)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 23:58:15 +02:00
Reece Russell 941c28ab0c readme: Add Golang URL to Go links 2022-08-22 19:45:15 +02:00
Bjørn Erik Pedersen ffbdcc75ab Update README.md 2022-08-19 19:15:58 +02:00
Bjørn Erik Pedersen 95d9764518 Update README.md 2022-08-19 19:12:43 +02:00
Bjørn Erik Pedersen b66f9f2665 Update README.md 2022-08-19 19:12:04 +02:00
Anthony Fok f7e00c039f github: Use GitHub's Choco-Install function to retry installs
Apparently, two instances of "choco install mingw" running simultaneously
(for go-version 1.18.x and 1.19.x) would often cause at least one of them
to fail to connect to SourceForge:

    ERROR: The remote file either doesn't exist, is unauthorized,
    or is forbidden for url
    'https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/ray_linn/GCC-10.X-with-ada/GCC-10.2.0-crt-8.0.0-with-ada-20201019.7z/download'.
    Exception calling "GetResponse" with "0" argument(s):
    "The request was aborted: Could not create SSL/TLS secure channel."

With Choco-Install however, it simply works every single time!

Thanks to keymanapp/api.keyman.com#156 for the great tip!

See the definition of the PowerShell Choco-Install function here:
https://github.com/actions/runner-images/blob/main/images/win/scripts/ImageHelpers/ChocoHelpers.ps1
2022-08-14 00:52:23 -06:00
Bjørn Erik Pedersen f5ba6fd45c common/hugio: One more fix for non-OS fs
Note that these are only used with OS fs, so no practical bugs, but still-
2022-08-13 19:22:45 +02:00
Bjørn Erik Pedersen c4bbc1eeeb common/hugio: Fix CopyDir when fs is not OS 2022-08-13 18:26:16 +02:00
Anthony Fok 0e0fb1b648 snap: Delete obsolete custom x-nodejs plugins 2022-08-12 21:39:25 -06:00
satotake b017f7cb01 livereload: Inject script without head or body tag
Currently, Hugo does not inject `livereload` script if html does not contain `<head>` or `<body>`. This sometimes happens if you create new sites without `theme` and it is hard to catch the cause soon.

This PR:

* Inject livereload script even if html does not include `<head>`, `<body>`, or `<html>`
    - Modern browsers execute scripts even if they are outside `<html>`
    - Some js frameworks (confirmed with vite) inject HRM script without `<html>` tag
* Append warning script to html if `<head>` or `<body>` is not in html
* Fix bug that livereload cannot be appended to the tags with attrs

Close #10105
2022-08-07 18:34:01 +02:00
Bjørn Erik Pedersen 7fb28085ac releaser: Fat MacOS binaries
Closes #9131
2022-08-03 23:22:01 +02:00
Bjørn Erik Pedersen 0cd1929b9c Update to Go 1.19
Fixes #10145
2022-08-03 23:22:01 +02:00
piyo cbdaff2135 markup/goldmark/codeblock: Fix attributes when no language identifier in CodeBlock
Fixes #10118
2022-08-03 11:32:08 +02:00
杨晴 3fefea06b8 commands: Fix embed in livereload.go 2022-08-01 11:42:26 +02:00
Bjørn Erik Pedersen 5c48ba9343 Update README.md
Updates #10136
2022-07-31 15:41:42 +02:00
Takeshi Sato 21562e3aad Externalise and embed livereload.js string
This is a small refactoring. `livereload/livereload.go` has a wide
column. Sometimes language server does not work because of it.
Create a new js file and embed it.
2022-07-26 13:51:08 +02:00
Bjørn Erik Pedersen 9c24b86e4b Cache when not found in LookupLayout
Very visible when using the pprof mutex profiler.

```bash
name         old time/op    new time/op    delta
Baseline-10    58.4ms ± 1%    51.6ms ± 0%  -11.56%  (p=0.029 n=4+4)

name         old alloc/op   new alloc/op   delta
Baseline-10    64.3MB ± 0%    64.2MB ± 0%     ~     (p=0.114 n=4+4)

name         old allocs/op  new allocs/op  delta
Baseline-10      649k ± 0%      649k ± 0%     ~     (p=0.229 n=4+4)
```
2022-07-13 15:49:00 +02:00
Bjørn Erik Pedersen 223bf28004 parser/pageparser: Don't store the byte slices
On its own this change doesn't do any magic, but this is part of a bigger picture about making Hugo leaner in the
memory usage department.
2022-07-09 16:03:11 +02:00
Bjørn Erik Pedersen 72b0ccdb01 Make the baseline benchmark's test files stable
And add some assertions.
2022-06-30 12:36:55 +02:00
Bjørn Erik Pedersen bdf935d66c Squashed 'docs/' changes from 30f32a624..e5aa641a6
e5aa641a6 remove erroneous foo
8307b474e Add hl_inline to highlight function documentation
8517b02d9 update GitHub 404 docs URL to prevent redirect
5c52c2715 remove aerobatic.com
3eb179558 Update syntax-highlighting.md
00cbe3f2f Check for nil Chroma .Aliases
45416515e netlify: Hugo 0.101.0
d5dcd1581 docs: Regen docshelper
4785e99b1 common: Add hugo.GoVersion
e679f1b7e resources/page: Add :slugorfilename attribute
f2cc72a1f Update spellcheck.yml (#1755)

git-subtree-dir: docs
git-subtree-split: e5aa641a62
2022-06-28 20:51:33 +02:00
Bjørn Erik Pedersen 65e52a7f5c Merge commit 'bdf935d66c1f02dfc942a30e9fc00519bba3aacb' 2022-06-28 20:51:33 +02:00
Anthony Fok 8ebcaa5394 Accept vendor-specified build date if .git/ is unavailable
Fixes #10053
2022-06-28 20:51:18 +02:00
Anthony Fok 241481931f snap: Replace mage with "go build" and set VendorInfo=snap 2022-06-28 04:09:50 -06:00
Anthony Fok 5caed8a71f snap: Use interface names etc-gitconfig and gitconfig, Take 2
as recommended by @alexmurray to keep consistency with other snaps; see
https://forum.snapcraft.io/t/system-files-and-personal-files-request-for-hugo/30598

I neglected to make the rename of system-gitconfig → etc-gitconfig and
user-gitconfig → gitconfig to the apps.plugs section in commit fd3953c,
hence this commit to fix the store upload scan failure:

  interface 'system-gitconfig' not found in base declaration
  interface 'user-gitconfig' not found in base declaration
  unknown plugs interface name reference 'system-gitconfig'
  unknown plugs interface name reference 'user-gitconfig'

Special thanks to Alex Murray and Igor Ljubuncic for approving the
system-files and personal-files request for Hugo.

Fixes #6226
2022-06-27 09:27:30 -06:00
Bjørn Erik Pedersen d1278f696a Extract the baseline benchmark to a test 2022-06-26 09:56:01 +02:00
Bjørn Erik Pedersen 92f31ae63b Add a baseline benchmark 2022-06-25 18:26:40 +02:00
Anthony Fok fd3953c18c snap: Use interface names etc-gitconfig and gitconfig
as recommended by @alexmurray to keep consistency with other snaps; see
https://forum.snapcraft.io/t/system-files-and-personal-files-request-for-hugo/30598/2

One closer step towards fixing #6226
2022-06-24 00:59:37 -06:00
hugoreleaser 15463f835b releaser: Prepare repository for 0.102.0-DEV
[ci skip]
2022-06-16 07:51:58 +00:00
hugoreleaser 466fa43c16 releaser: Bump versions for release of 0.101.0
[ci skip]
2022-06-16 07:09:16 +00:00
hugoreleaser 6072ce0bcd releaser: Add release notes for 0.101.0
[ci skip]
2022-06-16 07:09:16 +00:00
Bjørn Erik Pedersen 2c5943ddc6 build: Update to Go 1.18.3
Fixes #9964
2022-06-16 08:27:08 +02:00
Bjørn Erik Pedersen 0cb459a20a docs: Regen docshelper 2022-06-16 07:23:38 +02:00
Bjørn Erik Pedersen 475f87f685 Squashed 'docs/' changes from 96fdc246c..30f32a624
30f32a624 Update scss-sass.md
503130186 Update scss-sass.md
cbfaa6704 Remove references to outdated Blackfriday markdown processor (#1748)
e43948d26 netlify: Hugo 0.100.2
aaabe0ee9 Merge branch 'tempv0.100.2'
bc615f257 tpl/path: Add path.BaseName function
5dddbf95e Add Chinese translation for _documentation and _index.md
b1e4f3200 Add translation for search tools
23281633a Update introduction.md
3abb5fb5d netlify: Hugo 0.100.1
644ed3b01 Update faq.md
54f377051 Add some lines about resources.Copy
82cf08355 Remove most Blackfriday references
bfbe5aaf3 netlify: Update to Hugo 0.100.0
8b6f8577f docs: Regen CLI docs
858a5d39c docs: Regen docs helper
5037d504f Merge commit 'e4bfe59c4e043c92d3992587d8c64d264b262a22'
03ca7be5b Fix indentation in highlight shortcode

git-subtree-dir: docs
git-subtree-split: 30f32a6243
2022-06-16 07:22:11 +02:00
Bjørn Erik Pedersen 604cfffc5b Merge commit '475f87f685439de0f907a9ffc29bfd1361eb1c59' 2022-06-16 07:22:11 +02:00
Bjørn Erik Pedersen d863dde6c6 markup/highlight: Add hl_inline option
Closes #9442
Closes #9635
Closes #9638
2022-06-15 20:04:20 +02:00
Bjørn Erik Pedersen 580b214a4c deps: Update github.com/alecthomas/chroma/v2 v2.1.0 => v2.2.0 2022-06-15 20:04:20 +02:00
dependabot[bot] ddb9547085 build(deps): bump github.com/clbanning/mxj/v2 from 2.5.5 to 2.5.6
Bumps [github.com/clbanning/mxj/v2](https://github.com/clbanning/mxj) from 2.5.5 to 2.5.6.
- [Release notes](https://github.com/clbanning/mxj/releases)
- [Commits](https://github.com/clbanning/mxj/compare/v2.5.5...v2.5.6)

---
updated-dependencies:
- dependency-name: github.com/clbanning/mxj/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 14:19:26 +02:00
dependabot[bot] 288b0fb15a build(deps): bump github.com/pelletier/go-toml/v2 from 2.0.1 to 2.0.2
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.1 to 2.0.2.
- [Release notes](https://github.com/pelletier/go-toml/releases)
- [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml)
- [Commits](https://github.com/pelletier/go-toml/compare/v2.0.1...v2.0.2)

---
updated-dependencies:
- dependency-name: github.com/pelletier/go-toml/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 14:17:56 +02:00
dependabot[bot] 3e1344632d build(deps): bump golang.org/x/tools from 0.1.10 to 0.1.11
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.10 to 0.1.11.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.1.10...v0.1.11)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 12:12:23 +02:00
dependabot[bot] 7a9ce0eca5 build(deps): bump github.com/tdewolff/minify/v2 from 2.11.5 to 2.11.10
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.11.5 to 2.11.10.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.11.5...v2.11.10)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 12:11:23 +02:00
dependabot[bot] f2ba0cc8c5 build(deps): bump github.com/evanw/esbuild from 0.14.42 to 0.14.43
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.42 to 0.14.43.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.42...v0.14.43)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 12:10:55 +02:00
dependabot[bot] 62ceaabdce build(deps): bump github.com/getkin/kin-openapi from 0.94.0 to 0.97.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.94.0 to 0.97.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.94.0...v0.97.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-15 10:36:59 +02:00
Bjørn Erik Pedersen 35fa192838 deps: Udpate to github.com/alecthomas/chroma/v2
Fixes #9932
Fixes #9931
2022-06-14 14:08:40 +02:00
Khayyam Saleem 09ac733381 common: Add hugo.GoVersion
Closes #9849. This enables `hugo.GoVersion` in templates to access the
version of Go that the Hugo binary was built with.
2022-06-14 09:48:45 +02:00
Bjørn Erik Pedersen 66da1b7b2f resources: Panic on Copy of Resource with .Err
Fixes #10006
2022-06-14 09:47:41 +02:00
Dawid Potocki 5a9ecb82a3 resources/page: Add :slugorfilename attribute
Fixes #4739, #385
2022-06-13 21:45:03 +02:00
Bjørn Erik Pedersen cbc35c48d2 Respect NO_COLOR
Fixes #10004
2022-06-13 18:07:37 +02:00
Andreas Deininger 44f3c07969 readme: Update dependency list 2022-06-13 13:16:45 +02:00
Bjørn Erik Pedersen a5a4422aae Fix relURL with leading slash when baseURL includes a subdirectory
Fixes #9994
2022-06-13 11:40:25 +02:00
Bjørn Erik Pedersen 617e094482 js: Resolve index.esm.js
Same logic as for `index.{js,ts...}` files applies; if both `index.esm.js` and `index.js` exists (unlikely), you need to use the name
with extension when importing, else the `index.js` will win.

Fixes #8631
2022-06-12 20:09:37 +02:00
Bjørn Erik Pedersen cf12fa6161 Add animated GIF support
Note that this is for GIFs only (and not Webp).

Fixes #5030
2022-06-12 18:17:08 +02:00
Bjørn Erik Pedersen 2e1c81770a resources: Add a Gif source file to golden tests 2022-06-12 16:29:19 +02:00
hugoreleaser 4276075c78 releaser: Prepare repository for 0.101.0-DEV
[ci skip]
2022-06-08 11:07:37 +00:00
hugoreleaser d25cb2943f releaser: Bump versions for release of 0.100.2
[ci skip]
2022-06-08 10:25:57 +00:00
hugoreleaser 8b9bdc403d releaser: Add release notes for 0.100.2
[ci skip]
2022-06-08 10:25:57 +00:00
Bjørn Erik Pedersen 4e94d1db72 Update CONTRIBUTING.md 2022-06-07 17:22:17 +02:00
Bjørn Erik Pedersen 0566bbf7c7 Fix raw TOML dates in where/eq
Note that this has only been a problem with "raw dates" in TOML files in /data and similar. The predefined front matter
dates `.Date` etc. are converted to a Go Time and has worked fine even after upgrading to v2 of the go-toml lib.

Fixes #9979
2022-06-07 13:02:58 +02:00
Anthony Fok 534e7155bb deps: Update to github.com/pelletier/go-toml/v2 v2.0.1 2022-06-06 09:36:43 +02:00
Joe Mooring 953f215f32 tpl/path: Add path.BaseName function
Closes #9973
2022-06-06 09:36:16 +02:00
satotake 8e2fd55923 livereload: Use X-Forwarded-Host for Codespace
Codespace has 2 types of usage

1. in browser
2. vscode on local computer

As long as you select 2 (on local), Hugo handles livereload expectedly.
But if you use it in browser, Hugo does not reload on file change, as #9936 said.

This issue happens because `CheckOrigin` always fails.
Remote server could rewrite request host name.
Fix this by respecting `X-Forwarded-Host` header during origin checking

After merging this, you can preview changes lively with codespaece in browser.

```sh
hugo server --liveReloadPort 443
```

Close #9936
2022-06-05 11:16:24 +02:00
Bjørn Erik Pedersen 311b8008bf helpers: Fix panic with invalid defaultMarkdownHandler
Fixes #9968
2022-06-04 17:47:20 +02:00
Paul van Brouwershaven c7d5f9f067 resources: Register MediaTypes before build
Fixes #9971
2022-06-03 21:37:50 +02:00
hugoreleaser bfebd8c02c releaser: Prepare repository for 0.101.0-DEV
[ci skip]
2022-06-01 10:54:16 +00:00
hugoreleaser 0afb4866e3 releaser: Bump versions for release of 0.100.1
[ci skip]
2022-06-01 10:11:48 +00:00
hugoreleaser b1ec0c226f releaser: Add release notes for 0.100.1
[ci skip]
2022-06-01 10:11:48 +00:00
Bjørn Erik Pedersen 212d9e3017 Fix panic with markdownify/RenderString with shortcode on Page with no content file
Fixes #9959
2022-06-01 12:04:55 +02:00
hugoreleaser 4daac654d9 releaser: Prepare repository for 0.101.0-DEV
[ci skip]
2022-05-31 09:19:15 +00:00
hugoreleaser 27b077544d releaser: Bump versions for release of 0.100.0
[ci skip]
2022-05-31 08:37:12 +00:00
hugoreleaser 0f8343a2c2 releaser: Add release notes for 0.100.0
[ci skip]
2022-05-31 08:37:12 +00:00
Bjørn Erik Pedersen 3fcbee261a docs: Regen CLI docs 2022-05-31 10:24:01 +02:00
Bjørn Erik Pedersen db9d274275 docs: Regen docs helper 2022-05-31 10:23:20 +02:00
Bjørn Erik Pedersen 95baafeac4 Merge commit 'e4bfe59c4e043c92d3992587d8c64d264b262a22' 2022-05-31 10:22:05 +02:00
Bjørn Erik Pedersen e4bfe59c4e Squashed 'docs/' changes from 2d9da3a56..96fdc246c
96fdc246c Fix outdated i18n warnings flag mention on the multilingual page
39d197345 netlify: Hugo 0.99.1
fafd5070e Typo: Vriables > Variables
348c4e237 Update postcss.md
924fa76b6 netlify: Hugo 0.99.0
bb3f02ac1 Merge branch 'tempv0.99.0'
bdd0b6eaf Update usage.md
f562ff99d postcss: Fix import error handling
5d73f215f docs: Regen CLI docs

git-subtree-dir: docs
git-subtree-split: 96fdc246cc
2022-05-31 10:21:09 +02:00
Bjørn Erik Pedersen 6f7bf3f2d7 Fix indentation in highlight shortcode
This commit adds a new `.InnerDeindent` method to the shortcode context, which is `.Inner` with any
indendation removed. This is then used in the built-in `highlight` shortcode to prevent the extra
whitespace getting hightlighted.

Fixes #4717
2022-05-31 09:05:54 +02:00
Bjørn Erik Pedersen 9e904d756b Make .RenderString render shortcodes
Fixes #6703
2022-05-30 11:32:55 +02:00
Bjørn Erik Pedersen d2cfaede5b Improve shortcode indentation handling
* Record the leading whitespace (tabs, spaces) before the shortcode when parsing the page.
* Apply that indentation to the rendered result of shortcodes without inner content (where the user will apply indentation).

Fixes #9946
2022-05-30 11:32:55 +02:00
Bjørn Erik Pedersen 322d19a81f Add Markdown as an output format
The motivation behind this is not to make it easier to publish Markdown files, as that sounds unusual.

This is mainly meant for shortcodes that produces Markdown to be inlined.

You would do this by creating shortcodes with `*.md` suffix (e.g. `layouts/shortcodes/myshortcode.md`).

This output format is defined as plain text, and will use Go's much more lenient text template parser.

Updates #9821
2022-05-30 11:32:55 +02:00
dependabot[bot] 7cb484e121 build(deps): bump github.com/evanw/esbuild from 0.14.39 to 0.14.42
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.39 to 0.14.42.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.39...v0.14.42)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-30 11:32:04 +02:00
Bjørn Erik Pedersen 0b395f0b41 Run go mod tidy 2022-05-29 23:05:16 +02:00
Bjørn Erik Pedersen c1a83076bf Add a shortcode benchmark 2022-05-29 15:21:08 +02:00
Bjørn Erik Pedersen 0f8dc47037 Remove Blackfriday markdown engine
It has been deprecated for a long time, its v1 version is not maintained anymore, and there are many known issues. Goldmark should be
a mature replacement by now.

Closes #9934
2022-05-29 11:50:58 +02:00
Bjørn Erik Pedersen 3b478f50b7 Fix HasMenuCurrent and IsDescendant/IsAncestor when comparing to itself
There may be sites in the wild that depends on the faulty behaviour of IsDescendant/IsAncestor when comparing to itself, but

* The documentation and common sense says that a thing cannot be descendant or ancestor to itself.
* The bug introduced in `HasMenuCurrent` comes directly from that confusion.

Fixes #9846
2022-05-28 10:56:54 +02:00
dependabot[bot] f343b8eb78 build(deps): bump github.com/sanity-io/litter from 1.5.4 to 1.5.5
Bumps [github.com/sanity-io/litter](https://github.com/sanity-io/litter) from 1.5.4 to 1.5.5.
- [Release notes](https://github.com/sanity-io/litter/releases)
- [Changelog](https://github.com/sanity-io/litter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sanity-io/litter/compare/v1.5.4...v1.5.5)

---
updated-dependencies:
- dependency-name: github.com/sanity-io/litter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-27 20:04:35 +02:00
Bjørn Erik Pedersen 60ede146b6 deps: Update to github.com/tdewolff/minify/v2 v2.11.5 2022-05-27 19:12:20 +02:00
Bjørn Erik Pedersen dd9eaf19fd Don't use the baseURL /path as part of the resource cache key
As that prevents Hugo projects with sub paths in their `baseURL` to use themes with cached resources.

Fixes #9787
2022-05-27 19:11:16 +02:00
Bjørn Erik Pedersen 46a2ea6d0d postcss: Make the resource cache key more stable
By using the input map as the basis, which means the hash will not change if we add/rename/remove options.

This happened in Hugo 0.99, as we added a new options. This is unortunate.

Unfortunately this means that the cache keys for PostCSS will change one more time in 0.100, but will be stable going forward.

Note that we have implemented this pattern in all the other resource transformers.

Updates #9787
2022-05-27 18:38:44 +02:00
Bjørn Erik Pedersen 653ab2cc1f commands: Fix case where languages cannot be configured
There are some commands that needs to complete without a complete configuration.
2022-05-27 13:34:20 +02:00
Bjørn Erik Pedersen 52edea0fec github: Set HUGO_BUILD_TAGS: extended when running tests
Also fix TestDecodeConfig/Basic which started to fail in the extended build in 0.99.1.

Closes #9935
2022-05-27 11:55:03 +02:00
Cameron Moore 6a5acd753a metrics: Fix divide by zero error
Under certain conditions, `howSimilarString` could reach a divide-by-
zero situation which causes bogus values to print in the cache potential
column of the template hints output.  This situation essentially causes
a `int(math.NaN())` value to be returned and hilarity ensues thereafter.
2022-05-26 10:39:29 +02:00
Bjørn Erik Pedersen 805b21555e Fix error message when PostCSS config file is not found
Fixes #9927
2022-05-25 21:21:28 +02:00
Bjørn Erik Pedersen 8ca7052528 server: Skip watching dirs in ignoreFiles
Fixes #9838
2022-05-25 18:31:00 +02:00
Bjørn Erik Pedersen bb232a351a resources: Improve error message on .Resize etc. on SVGs
Fixes #9875
2022-05-25 18:06:20 +02:00
Bjørn Erik Pedersen 3854a6fa6c Fix Plainify edge cases
This commit replaces the main part of `helpers.StripHTML` with Go's implementation in its html/template package.

It's a little slower, but correctness is more important:

```bash
BenchmarkStripHTMLOld-10    	  680316	      1764 ns/op	     728 B/op	       4 allocs/op
BenchmarkStripHTMLNew-10    	  384520	      3099 ns/op	    2089 B/op	      10 allocs/op
```

Fixes #9199
Fixes #9909
Closes #9410
2022-05-25 17:55:23 +02:00
Bjørn Erik Pedersen cd0112a05a Add resources.Copy
Implemented by most Resource objects, but not Page (for now).

Fixes #9313
2022-05-25 10:35:31 +02:00
satotake 6f7fbe03b1 basefs: add noBuildLock flag
If the flag is enabled, `.hugo_build.lock` will not be created.
This ensures safe running on read-only filesystem etc.
Close #9780
2022-05-24 13:57:23 +02:00
satotake 2fc2e9c871 import: Fix importing jekyll site
Just use `afero.OsFs` without building site.
Close #9817
2022-05-21 23:44:40 +02:00
hugoreleaser e164834f0a releaser: Prepare repository for 0.100.0-DEV
[ci skip]
2022-05-18 12:01:09 +00:00
hugoreleaser d524067382 releaser: Bump versions for release of 0.99.1
[ci skip]
2022-05-18 11:18:14 +00:00
hugoreleaser 31ce89f7f4 releaser: Add release notes for 0.99.1
[ci skip]
2022-05-18 11:18:14 +00:00
Bjørn Erik Pedersen ee55fde5e5 releaser: Fix version replacement 2022-05-18 13:12:42 +02:00
Bjørn Erik Pedersen 2f9eac480f server: Fix multihost crash
As introduced in v0.99.0.

Fixes #9901
2022-05-18 11:10:04 +02:00
Bjørn Erik Pedersen 3a8189ee93 Update stale.yml 2022-05-17 13:57:01 +02:00
hugoreleaser 7bc3401eb5 common/hugo: Fix version logic
[ci skip]
2022-05-16 13:01:52 +02:00
hugoreleaser 1de333e7a3 releaser: Bump versions for release of 0.99.0
[ci skip]
2022-05-16 08:10:56 +00:00
hugoreleaser 35cb6eefb2 releaser: Add release notes for 0.99.0
[ci skip]
2022-05-16 08:10:55 +00:00
Bjørn Erik Pedersen 657d1a2d95 server: Refresh the error template
We cannot cache it forever, as that will not allow the end user to edit and preview it.
2022-05-16 09:24:30 +02:00
Bjørn Erik Pedersen 87a22eb6d6 server: Fix SIGINT handling after loading bad configuration
Also fix the config error messages.

Fixes #9664
2022-05-15 22:58:05 +02:00
Bjørn Erik Pedersen fc9f315d86 Improve SASS errors
Fixes #9897
2022-05-15 20:25:25 +02:00
Bjørn Erik Pedersen 4b189d8fd9 postcss: Fix import error handling
Note that we will now fail if `inlineImports` is enabled and we cannot resolve an import.

You can work around this by either:

* Use url imports or imports with media queries.
* Set `skipInlineImportsNotFound=true` in the options

Also get the argument order in the different NewFileError* funcs in line.

Fixes #9895
2022-05-15 20:25:25 +02:00
dependabot[bot] c2fa0a3320 build(deps): bump github.com/fsnotify/fsnotify from 1.5.3 to 1.5.4
Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.5.3 to 1.5.4.
- [Release notes](https://github.com/fsnotify/fsnotify/releases)
- [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsnotify/fsnotify/compare/v1.5.3...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/fsnotify/fsnotify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-14 15:01:01 +02:00
Bjørn Erik Pedersen 48ea24f89a common/herrors: Remove unused struct 2022-05-14 13:56:28 +02:00
dependabot[bot] 9f563856c1 build(deps): bump github.com/evanw/esbuild from 0.14.38 to 0.14.39
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.38 to 0.14.39.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.38...v0.14.39)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-14 13:45:11 +02:00
Bjørn Erik Pedersen 5c96bda70a errors: Misc improvements
* Redo the server error template
* Always add the content file context if relevant
* Remove some now superflous error string matching
* Move the server error template to _server/error.html
* Add file context (with position) to codeblock render blocks
* Improve JS build errors

Fixes #9892
Fixes #9891
Fixes #9893
2022-05-14 13:40:56 +02:00
Bjørn Erik Pedersen 4a96df96d9 server: Always rebuild the files involved in an error
Fixes #9884
2022-05-14 13:40:56 +02:00
Bjørn Erik Pedersen e8537e6dd0 postcss: Fix line numbers in error messages
Fixes #9880
2022-05-14 13:40:55 +02:00
Bjørn Erik Pedersen 2fbdee7268 Update CONTRIBUTING.md
Add some info about commit subject prefixes.
2022-05-13 11:42:12 +02:00
Bjørn Erik Pedersen 91fe1b6c69 js: Bump test dependency
To work around a cache issue.
2022-05-13 11:08:23 +02:00
Bjørn Erik Pedersen 7de629121d deps: Update github.com/spf13/cast v1.4.1 => v1.5.0 2022-05-11 17:03:28 +02:00
Bjørn Erik Pedersen 9d7f166244 hugolib: Check for nil in shouldRender 2022-05-09 15:53:25 +02:00
Bjørn Erik Pedersen 51f08b0b6a Revise the use of htime.Since/htime.Now
We cannot (also, it doesn't add any value)  use that when the `clock` is set,

* To measure time (before that global is set)
* To compare file timestamps re cache eviction

Fixes #9868
2022-05-09 14:18:40 +02:00
Bjørn Erik Pedersen 860c51c314 tpl/collections: Make sort stable
Fixes #9865
2022-05-08 20:45:54 +02:00
Bjørn Erik Pedersen 855e5869c6 docs: Regen CLI docs 2022-05-08 16:57:29 +02:00
Bjørn Erik Pedersen 327aaed6d8 Squashed 'docs/' changes from 7030fe3a2..2d9da3a56
2d9da3a56 Update build-options.md
5d910e24d Update introduction.md
111482a8d netlify: Hugo 0.98.0
d5505ca6f docs: Regen docs helper

git-subtree-dir: docs
git-subtree-split: 2d9da3a563
2022-05-08 16:56:42 +02:00
Bjørn Erik Pedersen 1c7759028e Merge commit '327aaed6d8ca57d8e5e3acb99ff53402ff1c556d' 2022-05-08 16:56:42 +02:00
Bjørn Erik Pedersen 35c88a7f90 Use configured timeZone for the clock
And some other related adjustments.

Updates #8787
2022-05-08 16:56:26 +02:00
satotake e77ca3c105 Add clock cli flag
Close #8787
2022-05-08 16:56:26 +02:00
Bjørn Erik Pedersen f2946da9e8 Improve error messages, esp. when the server is running
* Add file context to minifier errors when publishing
* Misc fixes (see issues)
* Allow custom server error template in layouts/server/error.html

To get to this, this commit also cleans up and simplifies the code surrounding errors and files. This also removes the usage of `github.com/pkg/errors`, mostly because of https://github.com/pkg/errors/issues/223 -- but also because most of this is now built-in to Go.

Fixes #9852
Fixes #9857
Fixes #9863
2022-05-06 19:43:22 +02:00
Bjørn Erik Pedersen 6eea32bd6b tpl: Improve godoc 2022-05-06 19:43:22 +02:00
nathannaveen a6d545854a github: Add permissions to test action 2022-05-03 09:23:47 +02:00
Bjørn Erik Pedersen e5f2173169 tpl/crypto: Add example for FNV32a 2022-04-28 17:47:17 +02:00
hugoreleaser 89c1655ec8 releaser: Prepare repository for 0.99.0-DEV
[ci skip]
2022-04-28 11:02:45 +00:00
hugoreleaser 165d299cde releaser: Bump versions for release of 0.98.0
[ci skip]
2022-04-28 10:23:30 +00:00
hugoreleaser e94dc6710a releaser: Add release notes for 0.98.0
[ci skip]
2022-04-28 10:23:30 +00:00
Bjørn Erik Pedersen a4fff5753f docs: Regen docs helper 2022-04-28 11:53:03 +02:00
Bjørn Erik Pedersen 3902f9a476 Squashed 'docs/' changes from 4c5edacfe..7030fe3a2
7030fe3a2 Remove .hvm file
dd0f98831 Add details for DigitalOcean (#1730)
ff0c8a6e7 netlify: Hugo 0.97.3
e254724a7 Delete hosting-on-bitbucket.md (#1723)
f4304fb7e Remove references to mmark (#1727)
255319023 remove broken link to codeplex.com (#1725)
06cc3856b delete broken link (#1724)
ec80dc069 Revert "netlify: Hugo 0.97.2"
211bad5de netlify: Hugo 0.97.2
cd549bca9 Revert "netlify: Update to Hugo 0.97.1" (#1722)
1039a0a48 netlify: Update to Hugo 0.97.1
a28e0d0d3 Update AddDate method
9077b443f Update lists.md
6b174329a Update lists.md
d87d9e296 netlify: Hugo 0.97.0
fa7db7c7e Merge branch 'tempv0.97.0'
dd6df54b5 Fix broken anchor links (#1717)
6b6743976 Remove StackImpact showcase
d00a7eda9 Fix for Cloudflare docs location (#1716)
890866722 Add git required FAQ & remove ZIP from Quick Start (#1713)
73e82d911 resources: Add `key` to reources.GetRemote options map
6795cd2a1 Correct possible typos re: Alias robots usage (#1165)
efd5d186a Hosting on Cloudflare Pages (#1709)
9239c5f2a deps: Update github.com/tdewolff/minify/v2 v2.10.0 => v2.11.0
859573bc8 Merge commit 'ec920363cdeb687c8bcac9c242767d366fb058cb'
21f108f12 docs: Regen CLI docs
9b6e7afd2 Add environment as a new filter to _cascade.target

git-subtree-dir: docs
git-subtree-split: 7030fe3a2e
2022-04-28 11:52:15 +02:00
Bjørn Erik Pedersen 4852a37653 Merge commit '3902f9a4767fe6e62ac5146728d8311b8cd227e0' 2022-04-28 11:52:15 +02:00
Bjørn Erik Pedersen fa80fe3c8a Some godoc adjustments and image struct renames 2022-04-27 23:53:56 +02:00
Bjørn Erik Pedersen 11047534e4 tpl/crypto: Add FNV32a
Main motivation to get a integer from a string.
2022-04-27 23:53:56 +02:00
Bjørn Erik Pedersen d7b54a4c37 markup/goldmark: Fix attribute nilpointer
Fixes 9819
2022-04-27 23:53:56 +02:00
Bjørn Erik Pedersen 13ceef7599 deps: Update to gocloud.dev v0.24.0
Note that the v0.25.0 version is broken (for Hugo, at least).
2022-04-27 12:12:43 +02:00
dependabot[bot] 942d0dd2c7 build(deps): bump github.com/mitchellh/mapstructure from 1.4.3 to 1.5.0
Bumps [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) from 1.4.3 to 1.5.0.
- [Release notes](https://github.com/mitchellh/mapstructure/releases)
- [Changelog](https://github.com/mitchellh/mapstructure/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitchellh/mapstructure/compare/v1.4.3...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/mitchellh/mapstructure
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-27 10:06:21 +02:00
Joe Mooring a022ca271b deps: Update github.com/yuin/goldmark v1.4.11 => v1.4.12
Fixes #9054
Fixes #9756
Fixes #9757
2022-04-27 10:04:00 +02:00
dependabot[bot] d56b33955f build(deps): bump github.com/evanw/esbuild from 0.14.36 to 0.14.38
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.36 to 0.14.38.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.36...v0.14.38)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-27 10:03:12 +02:00
Joe Mooring 55e28c239e deps: Update github.com/tdewolff/minify/v2 v2.11.1 => v2.11.2
Fixes #9820
2022-04-26 20:01:45 +02:00
Bjørn Erik Pedersen 9a888c243a Some godoc adjustments 2022-04-23 22:22:50 +02:00
Bjørn Erik Pedersen 05b45c35c8 tpl/lang: Handle nil values in lang.Merge 2022-04-23 22:22:50 +02:00
Bjørn Erik Pedersen 625be77e08 resources/page: Mark some more interface methods as internal
For the new documenttion.
2022-04-23 22:22:50 +02:00
Bjørn Erik Pedersen 097fd588ca Deprecate page.Author and page.Authors
These are not documented, and they don't belong on Page. We should consider having author a first class citizen of Hugo, but as it is not it's better modelled as a taxonomy.
2022-04-23 22:22:50 +02:00
hugoreleaser 41cc4e4ba3 releaser: Prepare repository for 0.98.0-DEV
[ci skip]
2022-04-18 18:01:38 +00:00
hugoreleaser 078053a43d releaser: Bump versions for release of 0.97.3
[ci skip]
2022-04-18 17:22:19 +00:00
hugoreleaser 7d9f888080 releaser: Add release notes for 0.97.3
[ci skip]
2022-04-18 17:22:19 +00:00
Bjørn Erik Pedersen 9b352f04a3 Fix syncing of /static regression
As introduced in Hugo `v0.76.1`.

And add a proper test for it.

Fixes  #9794
Closes #9788
2022-04-18 19:17:27 +02:00
Bjørn Erik Pedersen e66e2e9ce5 Revert "Revert "Fix PostProcess regression for hugo server""
This reverts commit 6c35a1a9ea.

Updates #9794
2022-04-18 19:17:27 +02:00
hugoreleaser 5de6f8a02c releaser: Prepare repository for 0.98.0-DEV
[ci skip]
2022-04-17 09:16:40 +00:00
hugoreleaser 5099abe600 releaser: Bump versions for release of 0.97.2
[ci skip]
2022-04-17 08:39:27 +00:00
hugoreleaser 99ec88d42d releaser: Add release notes for 0.97.2
[ci skip]
2022-04-17 08:39:27 +00:00
Bjørn Erik Pedersen 6c35a1a9ea Revert "Fix PostProcess regression for hugo server"
This reverts commit 4deb5c6066.
2022-04-17 10:35:01 +02:00
hugoreleaser 363bc907c0 releaser: Prepare repository for 0.98.0-DEV
[ci skip]
2022-04-16 17:35:04 +00:00
hugoreleaser 04efcb2a68 releaser: Bump versions for release of 0.97.1
[ci skip]
2022-04-16 16:57:30 +00:00
hugoreleaser 4560725521 releaser: Add release notes for 0.97.1
[ci skip]
2022-04-16 16:57:30 +00:00
Bjørn Erik Pedersen 4deb5c6066 Fix PostProcess regression for hugo server
Fixes #9788
2022-04-16 18:43:13 +02:00
Bjørn Erik Pedersen 397fce5603 Fix MediaType when reading images from cache
Fixes #8931
2022-04-16 16:36:15 +02:00
Bjørn Erik Pedersen 0093eaa683 deps: Upgrade github.com/bep/overlayfs v0.4.0 => v0.5.0
That version implement file.Readdir/Readdirnames for n > 0.

Closes #9783
2022-04-14 21:26:05 +02:00
hugoreleaser d0f731c039 releaser: Prepare repository for 0.98.0-DEV
[ci skip]
2022-04-14 09:24:30 +00:00
hugoreleaser c07f3626e7 releaser: Bump versions for release of 0.97.0
[ci skip]
2022-04-14 08:45:07 +00:00
hugoreleaser 42b5d16536 releaser: Add release notes for 0.97.0
[ci skip]
2022-04-14 08:45:07 +00:00
Bjørn Erik Pedersen d80d5a104f releaser: Reduce parallelism 2022-04-14 10:38:31 +02:00
Bjørn Erik Pedersen 842262f653 Revert "build(deps): bump gocloud.dev from 0.20.0 to 0.25.0"
This reverts commit 20162518c4.

Updates #9778
2022-04-13 17:29:36 +02:00
Bjørn Erik Pedersen 2dbdf38a54 resources: Add key to reources.GetRemote options map
If set, `key` will be used as the only cache key element for the resource.

The default behaviour is to calculate the key based on the URL and all the options.

This means that you can now do:

```
{{ $cacheKey := print $url (now.Format "2006-01-02") }}
{{ $resource := resource.GetRemote $url (dict "key" $cacheKey) }}
```

Fixes #9755
2022-04-13 09:18:17 +02:00
dependabot[bot] f8c4e1690a build(deps): bump github.com/evanw/esbuild from 0.14.34 to 0.14.36
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.34 to 0.14.36.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.34...v0.14.36)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-12 15:03:47 +02:00
Bjørn Erik Pedersen 627eed1d62 Make string sorting (e.g. ByTitle, ByLinkTitle and ByParam) language aware
Fixes #2180
2022-04-12 13:24:16 +02:00
Bjørn Erik Pedersen 82ba634ed9 Fix gosum checksum errors
Both of the 2 libraries in question have had known "double releases" -- same tag pushed twice with different commits.

Note that this will not resolve itself in Hugo "fully" before we do a new release (which should happen in a day or two).

Fixes #9771
2022-04-11 20:12:11 +02:00
dependabot[bot] d417a6cf74 build(deps): bump github.com/tdewolff/minify/v2 from 2.11.0 to 2.11.1
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.11.0 to 2.11.1.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.11.0...v2.11.1)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-11 11:48:11 +02:00
Bjørn Erik Pedersen 13dac7f3cd compare: Add a string sort benchmark 2022-04-11 11:33:58 +02:00
Bjørn Erik Pedersen 30c2e54c25 Replace all usage of CopyOnWriteFs with OverlayFs
Fixes #9761
2022-04-10 13:49:31 +02:00
Joe Mooring 3117e58595 deps: Update github.com/tdewolff/minify/v2 v2.10.0 => v2.11.0
Fixes #9713
Fixes #9740

Adds support for minify.tdewolff.svg.keepComments (bool)
2022-04-08 21:44:29 +02:00
Bjørn Erik Pedersen ec920363cd Squashed 'docs/' changes from 63386081c..4c5edacfe
4c5edacfe cSpell config update (#1700)
9df788b25 Fix broken link (hugo modules) (#1710)
9928a70d6 Fix workspace formatting (#1707)
55467e7c8 Update partials.md
9f4bd0023 Update formats.md
9b3913c86 Remove footnoteAnchorPrefix and footnoteReturnLinkContents (#1704)
94502a09b Code block render hooks are introduced in v0.93.0 (#1701)
c447270ef Update sitemap-template.md
78665c1e0 Update sitemap-template.md
60653c17d Update the caddy error docs link (#1696)
9a3675aad Update sitemap templates (#1699)
e0d08cdbb Add wpxr-to-static to list of migration tools (#1512)
b53eb5a08 Add page for deploying with rclone (#1511)
4207c57ff netlify: Hugo 0.96.0
a18d646ea docs: Regen docshelper
e3e0981ed docs: Regen CLI docs
fda988d01 Merge commit 'd276e901b36d2576ef8350ed96b17f66254eac1b'
e4a26dbca tpl/crypto: Add optional encoding arg to hmac function

git-subtree-dir: docs
git-subtree-split: 4c5edacfee
2022-04-08 13:32:01 +02:00
Bjørn Erik Pedersen 5b5dcb8d5a Merge commit 'ec920363cdeb687c8bcac9c242767d366fb058cb' 2022-04-08 13:32:01 +02:00
Bjørn Erik Pedersen ffe3eb9195 docs: Regen CLI docs 2022-04-08 13:31:50 +02:00
Bjørn Erik Pedersen d070bdf10f Rework the Destination filesystem to make --renderStaticToDisk work
See #9626
2022-04-08 13:26:17 +02:00
Bjørn Erik Pedersen b08193971a Revert "Revert "Some minor adjustments to the new static filesystem logic""
This reverts commit 5ef8a9f32c.
2022-04-08 13:26:17 +02:00
Bjørn Erik Pedersen 0a56f2af4e Revert "Revert "Allow rendering static files to disk and dynamic to memory in server mode""
This reverts commit 64b7b7a897.
2022-04-08 13:26:16 +02:00
dependabot[bot] 9e360d3844 build(deps): bump github.com/evanw/esbuild from 0.14.31 to 0.14.34
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.31 to 0.14.34.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.31...v0.14.34)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-07 11:38:43 +02:00
Joe Mooring a8c221d33b modules/client: Vendor config directory if present
Fixes #9751
2022-04-05 20:39:04 +02:00
Bjørn Erik Pedersen e58a540895 resources: Create a common ResourceFinder interface
And make both .Resources and resources implement it.

This gets us 2 new methods/functions, so you can now also do:

* .Resources.Get
* resources.ByType

Note that GetRemote is not covered by this interface, as that is only available as a global template function.

Fixes #8653
2022-04-05 18:00:44 +02:00
dependabot[bot] 20162518c4 build(deps): bump gocloud.dev from 0.20.0 to 0.25.0
Bumps [gocloud.dev](https://github.com/google/go-cloud) from 0.20.0 to 0.25.0.
- [Release notes](https://github.com/google/go-cloud/releases)
- [Commits](https://github.com/google/go-cloud/compare/v0.20.0...v0.25.0)

---
updated-dependencies:
- dependency-name: gocloud.dev
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 17:01:25 +02:00
dependabot[bot] 6b469cc8fb build(deps): bump golang.org/x/tools from 0.1.9 to 0.1.10
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.9 to 0.1.10.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.1.9...v0.1.10)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 16:14:00 +02:00
dependabot[bot] 080dcac6b8 build(deps): bump github.com/magefile/mage from 1.12.1 to 1.13.0
Bumps [github.com/magefile/mage](https://github.com/magefile/mage) from 1.12.1 to 1.13.0.
- [Release notes](https://github.com/magefile/mage/releases)
- [Changelog](https://github.com/magefile/mage/blob/master/.goreleaser.yml)
- [Commits](https://github.com/magefile/mage/compare/v1.12.1...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/magefile/mage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 16:13:27 +02:00
dependabot[bot] 072fc8cc34 build(deps): bump github.com/gobuffalo/flect from 0.2.4 to 0.2.5
Bumps [github.com/gobuffalo/flect](https://github.com/gobuffalo/flect) from 0.2.4 to 0.2.5.
- [Release notes](https://github.com/gobuffalo/flect/releases)
- [Commits](https://github.com/gobuffalo/flect/compare/v0.2.4...v0.2.5)

---
updated-dependencies:
- dependency-name: github.com/gobuffalo/flect
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 16:12:17 +02:00
Bjørn Erik Pedersen 658e11ebaa Localize all the GroupBy*Date methods
Fixes #9745
2022-04-05 16:11:11 +02:00
dependabot[bot] e0a882fd3b build(deps): bump github.com/getkin/kin-openapi from 0.93.0 to 0.94.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.93.0 to 0.94.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.93.0...v0.94.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 13:54:36 +02:00
David Jones d0657a436e deploy: Set an MD5 attribute and use that if eTag not available
During Hugo deploy when a remote MD5 is invalid (e.g due to multipart eTag) Hugo reads the entire remote file and calculates the MD5 again which can be slow.

This commit updates the file upload so that it will also store an MD5 hash in the cloud provider's attributes. e.g in AWS this looks like x-amz-meta-md5chksum: 26fe392386a8123bf8956a16e08cb841.
2022-04-05 10:42:54 +02:00
dependabot[bot] a6e2e38bb2 build(deps): bump github.com/sanity-io/litter from 1.5.2 to 1.5.4
Bumps [github.com/sanity-io/litter](https://github.com/sanity-io/litter) from 1.5.2 to 1.5.4.
- [Release notes](https://github.com/sanity-io/litter/releases)
- [Changelog](https://github.com/sanity-io/litter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sanity-io/litter/compare/v1.5.2...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/sanity-io/litter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 10:39:27 +02:00
dependabot[bot] 510e179003 build(deps): bump github.com/evanw/esbuild from 0.14.25 to 0.14.31
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.25 to 0.14.31.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.25...v0.14.31)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 09:42:11 +02:00
dependabot[bot] 21484f9e12 build(deps): bump github.com/frankban/quicktest from 1.14.2 to 1.14.3
Bumps [github.com/frankban/quicktest](https://github.com/frankban/quicktest) from 1.14.2 to 1.14.3.
- [Release notes](https://github.com/frankban/quicktest/releases)
- [Commits](https://github.com/frankban/quicktest/compare/v1.14.2...v1.14.3)

---
updated-dependencies:
- dependency-name: github.com/frankban/quicktest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 09:41:45 +02:00
Cathrine Paulsen da00e7714e Add environment as a new filter to _cascade.target
Fixes #9612
2022-04-05 09:41:24 +02:00
Bjørn Erik Pedersen ed9aa374dd Merge branch 'release-0.96.0' 2022-04-03 19:57:04 +02:00
Further 2a231b0b57 Snap: Add {system,user}-gitconfig plugs to read gitconfig (#9619)
* Add system-gitconfig plug to read /etc/gitconfig
* Add user-gitconfig plug to read user-specific gitconfig file(s)
* Add app environment setting to set $HOME as $SNAP_REAL_HOME
2022-04-01 02:52:39 -06:00
hugoreleaser 69c590080f releaser: Prepare repository for 0.97.0-DEV
[ci skip]
2022-03-26 09:53:13 +00:00
hugoreleaser 2fd4a7d3d6 releaser: Bump versions for release of 0.96.0
[ci skip]
2022-03-26 09:15:58 +00:00
hugoreleaser 5a4ac2dac1 releaser: Add release notes for 0.96.0
[ci skip]
2022-03-26 09:15:58 +00:00
Bjørn Erik Pedersen db1562e134 docs: Regen docshelper 2022-03-26 11:06:04 +02:00
Bjørn Erik Pedersen 5b18e10841 docs: Regen CLI docs 2022-03-26 11:05:30 +02:00
Bjørn Erik Pedersen d276e901b3 Squashed 'docs/' changes from a393f4cf4..63386081c
63386081c update cSpell config update
15c76494b Update cSpell custom dictionary (#1694)
34f3167b7 Update image processing (#1625)
7462cc798 fix: pipes in sample code break table creation (#1686)
48736447e Update anchorize.md
2ff0bd10b netlify: Hugo 0.95.0
0fc1d21b2 Update configuration.md
41855e372 Fix #1682
8c663433e Update related.md
7aa072eab netlify: Hugo 0.94.2
1682c7ee7 Update render-hooks.md
ce1283cc4 Move the Render Hooks doc to its own page
bbbbfbfc6 Update configuration-markup.md
92d91a316 Update configuration-markup.md
2e8068823 Update configuration-markup.md
ff2dbca60 Update configuration-markup.md
89d8e5d65 Add code block documenation
e993539f0 Update shortcodes.md
c1b28dbfe netlify: Hugo 0.94.1
81b8c9b83 Merge branch 'tempv0.94.1'
4763b3d50 docs: Regenerate CLI docs
b18463971 netlify: Bump to Hugo 0.94.0
4152ebc1d Merge branch 'tempv0.94.0'
ba3a11ac2 docs: Regenerate docshelper
e64016d13 docs: Regenerate docshelper
29180e4d2 add `.html` suffix to partial usage and references
3213e00f2 Docs tidy-up
6cfcae4b7 docs: Regenerate CLI docs
8a6cd0b4d docs: Regenerate docshelper
b20ab262f Merge commit 'd706529720b3b2ccb99719ccd578062ca25a0cc2'

git-subtree-dir: docs
git-subtree-split: 63386081c5
2022-03-26 11:04:57 +02:00
Bjørn Erik Pedersen d7497b28c1 Merge commit 'd276e901b36d2576ef8350ed96b17f66254eac1b' 2022-03-26 11:04:57 +02:00
Sara Op den Orth 94459680ba Deprecate .File.Extension
Fixes #9352
2022-03-26 10:03:36 +01:00
Bjørn Erik Pedersen 9202117ba0 resources: Add more details to .Err
This commit adds a .Data object (a map with `Body`, `StatusCode` etc.) to the .Err returned from `resources.GetRemote`, which means you can now do:

```
{{ with .Err }}
{{ range $k, $v := .Data }}
{{ end }}
{{ end }}
```

Fixes #9708
2022-03-25 16:40:36 +01:00
Joe Mooring a6fa290f67 commands: Change link protocol to https 2022-03-25 15:29:50 +01:00
dependabot[bot] 0bbc2fb523 build(deps): bump github.com/getkin/kin-openapi from 0.91.0 to 0.93.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.91.0 to 0.93.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.91.0...v0.93.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-25 11:08:23 +01:00
Joe Mooring 94e8a90769 tpl/crypto: Add optional encoding arg to hmac function
Closes #9709
2022-03-23 19:42:56 +01:00
Panagiotis Koursaris a461e9d01a Fix typo 2022-03-22 00:39:33 -06:00
cuishuang 48c98a8d24 Fix some typos
Signed-off-by: cuishuang <imcusg@gmail.com>
[foka@debian.org: Resolve merge conflict and squash 2 commits]
Signed-off-by: Anthony Fok <foka@debian.org>
2022-03-22 00:38:23 -06:00
Anthony Fok bbd455fe79 Update CONTRIBUTING.md to use "go install" to install mage
In Go 1.18, "go get" no longer builds packages.
2022-03-21 21:01:24 -06:00
Anthony Fok 8309a2b1c1 Dockerfile: Make it build with Go 1.18 2022-03-21 21:01:24 -06:00
Anthony Fok 2b7231097f snap: Make it build with Go 1.18 2022-03-21 21:01:24 -06:00
dependabot[bot] 13ff4ded76 build(deps): bump github.com/yuin/goldmark from 1.4.10 to 1.4.11
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.4.10 to 1.4.11.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.4.10...v1.4.11)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-21 17:31:27 +01:00
dependabot[bot] c3289eb5b8 build(deps): bump github.com/spf13/cobra from 1.3.0 to 1.4.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-21 14:08:38 +01:00
Bjørn Erik Pedersen 9539069f5e commands: Improve server startup to make tests less flaky
Do this by announcing/listen on the local address before we start the server.
2022-03-21 09:32:35 +01:00
Bjørn Erik Pedersen 0e305d6958 all: Use strings.Cut
Updates #9687
2022-03-21 09:32:35 +01:00
Luke de Waal 5adb81ce39 Support '-u=patch' in hugo mod get
The control-flow for running `hugo mod get` was adapted to allow for passing `-u=patch`
instead of only being able to pass `-u`.

Fixes #9127
2022-03-20 22:07:26 +01:00
Bjørn Erik Pedersen 1c0e7c1ae1 Make sure file mounts higher up wins
Fixes #9693
2022-03-20 20:23:03 +01:00
Joe Mooring cad2d8cc70 resources/images: Require width and height for Crop, Fill, and Fit
Closes #9696
2022-03-20 19:44:41 +01:00
Bjørn Erik Pedersen b80853de90 all: gofmt -w -r 'interface{} -> any' .
Updates #9687
2022-03-17 22:03:27 +01:00
Bjørn Erik Pedersen 423594e03a dartsass: Enable deprecation, @warn and @debug logging
* @warn and Sass deprecations are printed as WARN
* @debug is currently logged as INFO (needs the `--verbose` flag). We may adjust this if it gets too chatty.

Fixes #9683
2022-03-17 21:45:43 +01:00
Bjørn Erik Pedersen 64afb7ca51 Use revision etc. from debug.BuildInfo
Fixes #9680
2022-03-17 17:14:38 +01:00
hugoreleaser 004bec2e9a releaser: Prepare repository for 0.96.0-DEV
[ci skip]
2022-03-16 15:00:05 +00:00
hugoreleaser 9f2e76af65 releaser: Bump versions for release of 0.95.0
[ci skip]
2022-03-16 14:20:17 +00:00
hugoreleaser f1d1576822 releaser: Add release notes for 0.95.0
[ci skip]
2022-03-16 14:20:17 +00:00
Bjørn Erik Pedersen 5930173cd0 readme: Add note about Go 1.18 2022-03-16 13:20:30 +01:00
Bjørn Erik Pedersen 3476b53343 tpl: Pull in Go 1.18 patch that fixes the "no space in {{ continue }} and {{ break }}" bug 2022-03-16 13:14:23 +01:00
Bjørn Erik Pedersen e792d27017 readme: Add a contribution note 2022-03-16 13:09:17 +01:00
Bjørn Erik Pedersen 9d6495d774 github: Make it build with Go 1.18
I also ran some benchmarks comparing it to Go 1.17.8:

```bash
name                                      old time/op    new time/op    delta
SiteNew/Regular_Bundle_with_image-10        5.13ms ± 7%    4.23ms ± 7%  -17.47%  (p=0.029 n=4+4)
SiteNew/Regular_Bundle_with_JSON_file-10    5.12ms ± 1%    4.16ms ± 1%  -18.65%  (p=0.029 n=4+4)
SiteNew/Regular_Tags_and_categories-10      17.3ms ± 2%    15.1ms ± 1%  -12.53%  (p=0.029 n=4+4)
SiteNew/Regular_Canonify_URLs-10             106ms ± 1%      90ms ± 0%  -15.86%  (p=0.029 n=4+4)
SiteNew/Regular_Deep_content_tree-10        34.0ms ± 1%    30.0ms ± 2%  -11.72%  (p=0.029 n=4+4)
SiteNew/Regular_TOML_front_matter-10        22.7ms ± 2%    20.2ms ± 1%  -10.93%  (p=0.029 n=4+4)
SiteNew/Regular_Many_HTML_templates-10      16.7ms ± 0%    14.6ms ± 1%  -12.89%  (p=0.029 n=4+4)
SiteNew/Regular_Page_collections-10         22.2ms ± 1%    19.5ms ± 1%  -12.07%  (p=0.029 n=4+4)
SiteNew/Regular_List_terms-10               7.71ms ± 0%    6.59ms ± 1%  -14.52%  (p=0.029 n=4+4)

name                                      old alloc/op   new alloc/op   delta
SiteNew/Regular_Bundle_with_image-10        2.07MB ± 0%    2.04MB ± 0%   -1.40%  (p=0.029 n=4+4)
SiteNew/Regular_Bundle_with_JSON_file-10    1.85MB ± 0%    1.83MB ± 0%   -1.54%  (p=0.029 n=4+4)
SiteNew/Regular_Tags_and_categories-10      11.8MB ± 0%    11.1MB ± 0%   -6.11%  (p=0.029 n=4+4)
SiteNew/Regular_Canonify_URLs-10            95.8MB ± 0%    95.2MB ± 0%   -0.63%  (p=0.029 n=4+4)
SiteNew/Regular_Deep_content_tree-10        27.4MB ± 0%    26.9MB ± 0%   -1.68%  (p=0.029 n=4+4)
SiteNew/Regular_TOML_front_matter-10        15.2MB ± 0%    14.4MB ± 0%   -4.93%  (p=0.029 n=4+4)
SiteNew/Regular_Many_HTML_templates-10      8.45MB ± 0%    8.24MB ± 0%   -2.43%  (p=0.029 n=4+4)
SiteNew/Regular_Page_collections-10         16.5MB ± 0%    16.2MB ± 0%   -1.78%  (p=0.029 n=4+4)
SiteNew/Regular_List_terms-10               3.73MB ± 0%    3.64MB ± 0%   -2.30%  (p=0.029 n=4+4)

name                                      old allocs/op  new allocs/op  delta
SiteNew/Regular_Bundle_with_image-10         25.0k ± 0%     24.1k ± 0%   -3.60%  (p=0.029 n=4+4)
SiteNew/Regular_Bundle_with_JSON_file-10     25.1k ± 0%     24.1k ± 0%   -3.60%  (p=0.029 n=4+4)
SiteNew/Regular_Tags_and_categories-10        237k ± 0%      234k ± 0%   -1.29%  (p=0.029 n=4+4)
SiteNew/Regular_Canonify_URLs-10              365k ± 0%      363k ± 0%   -0.47%  (p=0.029 n=4+4)
SiteNew/Regular_Deep_content_tree-10          284k ± 0%      279k ± 0%   -1.98%  (p=0.029 n=4+4)
SiteNew/Regular_TOML_front_matter-10          252k ± 0%      249k ± 0%   -1.48%  (p=0.029 n=4+4)
SiteNew/Regular_Many_HTML_templates-10        117k ± 0%      115k ± 0%   -1.68%  (p=0.029 n=4+4)
SiteNew/Regular_Page_collections-10           184k ± 0%      181k ± 0%   -1.74%  (p=0.029 n=4+4)
SiteNew/Regular_List_terms-10                49.9k ± 0%     48.5k ± 0%   -2.79%  (p=0.029 n=4+4)
```
2022-03-16 11:32:39 +01:00
Bjørn Erik Pedersen 42cc5f88b6 tpl: Adjustments and an integration test for Go 1.18
Updates #9677
2022-03-16 10:36:46 +01:00
Bjørn Erik Pedersen a6488e7bad Remove Go 1.17 support
Go 1.18 had some breaking changes on source level meaning we cannot build on older Go versions anymore.

The improvements in Go 1.18 (e.g. `break` and `continue`)  were to good to pass on.

Note that you don't need Go (or Go 1.18) to run a pre-built binary.

Updates #9677
2022-03-16 08:57:58 +01:00
Bjørn Erik Pedersen 65a78cae1e tpl: Sync go_templates for Go 1.18
Using Go tag go1.18 4aa1efed4853ea067d665a952eee77c52faac774

Updates #9677
2022-03-16 08:54:25 +01:00
Bjørn Erik Pedersen 4d6d1d08da build: Bump to Go 1.18
Updates #9677
2022-03-16 08:43:18 +01:00
Bjørn Erik Pedersen b60e1bbdfe dartsass: Improve error message when no read access
Fixes #9662
2022-03-15 16:34:54 +01:00
Cathrine Paulsen 61cf3c9f63 Fix and refactor typos 2022-03-14 19:39:07 +01:00
Bjørn Erik Pedersen 31fbc081c9 Improve server startup/shutdown
Closes #9671
2022-03-14 19:38:17 +01:00
Bjørn Erik Pedersen cebd886ac1 commands: Improve server tests
Updates #9647
2022-03-14 09:16:33 +01:00
hugoreleaser 38f778cfcc releaser: Prepare repository for 0.95.0-DEV
[ci skip]
2022-03-12 11:02:38 +00:00
hugoreleaser 48fb9e4de3 releaser: Bump versions for release of 0.94.2
[ci skip]
2022-03-12 10:28:41 +00:00
hugoreleaser 0958167b38 releaser: Add release notes for 0.94.2
[ci skip]
2022-03-12 10:28:41 +00:00
Bjørn Erik Pedersen b37183e48d deps: Update github.com/yuin/goldmark v1.4.9 => v1.4.10
Fixes #9658
2022-03-12 11:24:11 +01:00
hugoreleaser 04ccde3e94 releaser: Prepare repository for 0.95.0-DEV
[ci skip]
2022-03-11 14:30:03 +00:00
hugoreleaser 0fcd9a5d87 releaser: Bump versions for release of 0.94.1
[ci skip]
2022-03-11 13:53:32 +00:00
hugoreleaser 4146084367 releaser: Add release notes for 0.94.1
[ci skip]
2022-03-11 13:53:32 +00:00
Bjørn Erik Pedersen 3bc742bea0 docs: Regenerate CLI docs 2022-03-11 14:47:27 +01:00
Bjørn Erik Pedersen 1a796d723c deps: Fix Goldmark regression with HTML comments
Fixes #9650
2022-03-11 14:44:01 +01:00
Bjørn Erik Pedersen 64b7b7a897 Revert "Allow rendering static files to disk and dynamic to memory in server mode"
This reverts commit 7d8011ed63.

Updates #9647
2022-03-11 13:10:47 +01:00
Bjørn Erik Pedersen 5ef8a9f32c Revert "Some minor adjustments to the new static filesystem logic"
This reverts commit 63bb2a5b18.
2022-03-11 13:10:47 +01:00
hugoreleaser 3bc34666c8 releaser: Prepare repository for 0.95.0-DEV
[ci skip]
2022-03-10 10:25:00 +00:00
hugoreleaser 63b236603e releaser: Bump versions for release of 0.94.0
[ci skip]
2022-03-10 09:46:35 +00:00
hugoreleaser b107b4b291 releaser: Add release notes for 0.94.0
[ci skip]
2022-03-10 09:46:35 +00:00
Bjørn Erik Pedersen a4ac188c19 docs: Regenerate docshelper 2022-03-10 10:08:04 +01:00
Bjørn Erik Pedersen b82d95575d Revert "markup/highlight: Add hl_inline option"
This reverts commit a360cab75a.
2022-03-10 10:06:22 +01:00
Bjørn Erik Pedersen 4e14cf7607 Fail with error when double-rendering text in markdownify/RenderString
This commit prevents the most commons case of infinite recursion in link render hooks when the `linkify` option is enabled (see below). This is always a user error, but getting a `stack overflow` (the current stack limit in Go is 1 GB on 64-bit, 250 MB on 32-bit) error isn't very helpful. This fix will not prevent all such errors, though, but we may do better once #9570 is in place.

So, these will fail:

```
<a href="{{ .Destination | safeURL }}" >{{ .Text | markdownify }}</a>
<a href="{{ .Destination | safeURL }}" >{{ .Text | .Page.RenderString }}</a>
```

`.Text` is already rendered to `HTML`. The above needs to be rewritten to:

```
<a href="{{ .Destination | safeURL }}" >{{ .Text | safeHTML }}</a>
<a href="{{ .Destination | safeURL }}" >{{ .Text | safeHTML }}</a>
```

Fixes #8959
2022-03-10 08:19:03 +01:00
Bjørn Erik Pedersen 5697348e17 markup/goldmark: Default to https for linkify
Fixes #9639
2022-03-09 22:30:10 +01:00
Joe Mooring f98e570b17 Add lang attribute to internal alias template
Closes #9586
2022-03-09 19:06:59 +01:00
Bjørn Erik Pedersen cdb8b0842b docs: Regenerate docshelper 2022-03-09 11:33:53 +01:00
Bjørn Erik Pedersen a360cab75a markup/highlight: Add hl_inline option
If set to true, the highlighted code will not be wrapped in any div.

Closes #9442
2022-03-09 11:33:13 +01:00
Joe Mooring c97fed08f9 minifiers: Make keepWhitespace = false default for HTML (note)
Closes #9456
2022-03-09 07:48:18 +01:00
Bjørn Erik Pedersen 53a6210d82 markup/goldmark/codeblocks: Fix slice bounds out of range
For the Position in code blocks we try to match the .Inner with the original source. This isn't always possible.

This commits avoids panics in these situations.

Fixes #9627
2022-03-08 21:50:21 +01:00
Bjørn Erik Pedersen 7182809d96 docs: Regenerate CLI docs 2022-03-08 19:40:50 +01:00
Bjørn Erik Pedersen 1f8cd2614d docs: Regenerate docshelper 2022-03-08 19:40:07 +01:00
Bjørn Erik Pedersen 9d76b8fa34 Merge commit 'd706529720b3b2ccb99719ccd578062ca25a0cc2' 2022-03-08 19:39:33 +01:00
Bjørn Erik Pedersen d706529720 Squashed 'docs/' changes from 3f95a2ace..a393f4cf4
a393f4cf4 Add a Spellcheck GitHub Action and config
8b6b1c381 netlify: Bump to Hugo 0.93.3
84515c183 Delete deployment-with-nanobox.md
dd45f9899 Fix typos in docs
e69de81a9 Update build-options.md
7745b7891 netlify: Hubo 0.93.2
037d63364 Clarify GitHub Pages Branches
94660c34b add missing %s
325de15e2 fix link to latest release note since the release notes were moved to GitHub: https://gohugo.io/news/no-more-releasenotes-here/
dbff41d01 Update introduction.md
0ecd627f7 netlify: Hugo 0.93.1
a74e16582 Update diagrams.md
33e310956 Add Goat example to test styling
fa0100a5b Update diagrams.md
64ac75367 Adjust diagram docs
f1d600044 Update theme
95bedff1a netlify: Bump to Hugo 0.93.0
849a8437f Merge commit 'c1398b91a9f4c67876b31feb67516b252e654d3c'
c0c60c43c docs: Regenerate docs helper
2c63fe518 cod: Regen CLI docs
f33ba4e5a CodeblockContext method renames
979b47968 Move the Goat template to the correct place
2df37e9e8 Add Markdown diagrams and render hooks for code blocks
bd8037d43 Allow images to be cropped without being resized
8b2af4b49 modules: Add modules.Workspace config for Go 1.18
46b99dea1 Add --printUnusedTemplates
1285302c9 commands: Rename --i18n-warnings to printI18nWarnings
dea2242c6 commands: Rename --path-warnings, --print-men to --printPathWarnings, --printMemoryUsage
db782ea46 deps: Update github.com/alecthomas/chroma v0.9.4 => v0.10.0

git-subtree-dir: docs
git-subtree-split: a393f4cf43
2022-03-08 19:37:17 +01:00
Bjørn Erik Pedersen 4576c82ed4 Cache reflect.MethodByName
The isolated benchmark for the function is obviously much faster:

```bash
name                old time/op    new time/op    delta
GetMethodByName-10    1.21µs ± 7%    0.23µs ± 5%   -81.42%  (p=0.029 n=4+4)

name                old alloc/op   new alloc/op   delta
GetMethodByName-10      680B ± 0%        0B       -100.00%  (p=0.029 n=4+4)

name                old allocs/op  new allocs/op  delta
GetMethodByName-10      20.0 ± 0%       0.0       -100.00%  (p=0.029 n=4+4)
```

But more pleasing is the overall performance looking at the site benchmarks:

```bash
name                                      old time/op    new time/op    delta
SiteNew/Regular_Bundle_with_image-10        6.25ms ± 2%    6.10ms ± 2%     ~     (p=0.057 n=4+4)
SiteNew/Regular_Bundle_with_JSON_file-10    6.30ms ± 2%    5.66ms ±11%     ~     (p=0.057 n=4+4)
SiteNew/Regular_Tags_and_categories-10      22.2ms ± 2%    17.4ms ± 1%  -21.88%  (p=0.029 n=4+4)
SiteNew/Regular_Canonify_URLs-10             108ms ± 0%     107ms ± 0%   -1.20%  (p=0.029 n=4+4)
SiteNew/Regular_Deep_content_tree-10        36.1ms ± 1%    33.8ms ± 1%   -6.44%  (p=0.029 n=4+4)
SiteNew/Regular_TOML_front_matter-10        24.9ms ± 1%    22.6ms ± 1%   -9.30%  (p=0.029 n=4+4)
SiteNew/Regular_Many_HTML_templates-10      17.9ms ± 1%    16.7ms ± 1%   -6.43%  (p=0.029 n=4+4)
SiteNew/Regular_Page_collections-10         23.3ms ± 1%    22.0ms ± 0%   -5.58%  (p=0.029 n=4+4)
SiteNew/Regular_List_terms-10               8.00ms ± 1%    7.63ms ± 0%   -4.62%  (p=0.029 n=4+4)

name                                      old alloc/op   new alloc/op   delta
SiteNew/Regular_Bundle_with_image-10        2.10MB ± 0%    2.07MB ± 0%   -1.46%  (p=0.029 n=4+4)
SiteNew/Regular_Bundle_with_JSON_file-10    1.88MB ± 0%    1.85MB ± 0%   -1.76%  (p=0.029 n=4+4)
SiteNew/Regular_Tags_and_categories-10      13.5MB ± 0%    11.6MB ± 0%  -13.99%  (p=0.029 n=4+4)
SiteNew/Regular_Canonify_URLs-10            96.1MB ± 0%    95.8MB ± 0%   -0.40%  (p=0.029 n=4+4)
SiteNew/Regular_Deep_content_tree-10        28.4MB ± 0%    27.3MB ± 0%   -3.83%  (p=0.029 n=4+4)
SiteNew/Regular_TOML_front_matter-10        16.9MB ± 0%    15.1MB ± 0%  -10.58%  (p=0.029 n=4+4)
SiteNew/Regular_Many_HTML_templates-10      8.98MB ± 0%    8.44MB ± 0%   -6.04%  (p=0.029 n=4+4)
SiteNew/Regular_Page_collections-10         17.1MB ± 0%    16.5MB ± 0%   -3.91%  (p=0.029 n=4+4)
SiteNew/Regular_List_terms-10               3.92MB ± 0%    3.72MB ± 0%   -5.03%  (p=0.029 n=4+4)

name                                      old allocs/op  new allocs/op  delta
SiteNew/Regular_Bundle_with_image-10         25.8k ± 0%     24.9k ± 0%   -3.49%  (p=0.029 n=4+4)
SiteNew/Regular_Bundle_with_JSON_file-10     25.8k ± 0%     24.9k ± 0%   -3.49%  (p=0.029 n=4+4)
SiteNew/Regular_Tags_and_categories-10        288k ± 0%      233k ± 0%  -18.90%  (p=0.029 n=4+4)
SiteNew/Regular_Canonify_URLs-10              375k ± 0%      364k ± 0%   -2.80%  (p=0.029 n=4+4)
SiteNew/Regular_Deep_content_tree-10          314k ± 0%      283k ± 0%   -9.77%  (p=0.029 n=4+4)
SiteNew/Regular_TOML_front_matter-10          302k ± 0%      252k ± 0%  -16.55%  (p=0.029 n=4+4)
SiteNew/Regular_Many_HTML_templates-10        133k ± 0%      117k ± 0%  -11.81%  (p=0.029 n=4+4)
SiteNew/Regular_Page_collections-10           202k ± 0%      183k ± 0%   -9.55%  (p=0.029 n=4+4)
SiteNew/Regular_List_terms-10                55.6k ± 0%     49.8k ± 0%  -10.40%  (p=0.029 n=4+4)
```

Thanks to @quasilyte for the suggestion.

Fixes 9386
2022-03-08 19:36:55 +01:00
Bjørn Erik Pedersen ff02d41721 releaser: Remove the GitHub link syntax around release contributors
Fixes #9581
2022-03-08 19:36:31 +01:00
Bjørn Erik Pedersen 63bb2a5b18 Some minor adjustments to the new static filesystem logic
Closes #9625
2022-03-08 19:27:54 +01:00
SatowTakeshi 7d8011ed63 Allow rendering static files to disk and dynamic to memory in server mode
Updates #9625
2022-03-08 19:27:54 +01:00
dependabot[bot] b9a1be2f99 build(deps): bump github.com/kyokomi/emoji/v2 from 2.2.8 to 2.2.9
Bumps [github.com/kyokomi/emoji/v2](https://github.com/kyokomi/emoji) from 2.2.8 to 2.2.9.
- [Release notes](https://github.com/kyokomi/emoji/releases)
- [Commits](https://github.com/kyokomi/emoji/compare/v2.2.8...v2.2.9)

---
updated-dependencies:
- dependency-name: github.com/kyokomi/emoji/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-08 16:10:04 +01:00
dependabot[bot] 0978245039 build(deps): bump github.com/evanw/esbuild from 0.14.23 to 0.14.25
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.23 to 0.14.25.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.23...v0.14.25)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-08 16:09:17 +01:00
dependabot[bot] ff37df830c build(deps): bump github.com/niklasfasching/go-org from 1.6.0 to 1.6.2
Bumps [github.com/niklasfasching/go-org](https://github.com/niklasfasching/go-org) from 1.6.0 to 1.6.2.
- [Release notes](https://github.com/niklasfasching/go-org/releases)
- [Commits](https://github.com/niklasfasching/go-org/compare/v1.6.0...v1.6.2)

---
updated-dependencies:
- dependency-name: github.com/niklasfasching/go-org
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-08 16:08:32 +01:00
dependabot[bot] 5857d5524a build(deps): bump github.com/getkin/kin-openapi from 0.90.0 to 0.91.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.90.0 to 0.91.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.90.0...v0.91.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-08 16:05:40 +01:00
hugoreleaser d86eca5bb2 releaser: Prepare repository for 0.94.0-DEV
[ci skip]
2022-03-08 10:50:30 +00:00
hugoreleaser 44e3c002a8 releaser: Bump versions for release of 0.93.3
[ci skip]
2022-03-08 10:15:27 +00:00
hugoreleaser 9177849f9b releaser: Add release notes for 0.93.3
[ci skip]
2022-03-08 10:15:27 +00:00
Bjørn Erik Pedersen 0e0d672bcb Remove the decorator from the fs used in ReadDir
There have been a site breakage reported in the wild after 0.93.1. With this commit we shoudl be back to how it behaved in 0.92.2.

Closes #9609
2022-03-08 09:51:59 +01:00
Bjørn Erik Pedersen 19f816f77e Update stale.yml
operations-per-run: 999

We need to get this done so the notification gets back to normal.
2022-03-08 09:09:38 +01:00
dependabot[bot] 970f385c48 build(deps): bump github.com/yuin/goldmark from 1.4.7 to 1.4.8
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.4.7 to 1.4.8.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.4.7...v1.4.8)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-07 09:59:16 +01:00
Bjørn Erik Pedersen 76c1248f70 Remove the examples/ folder
These are not maintained and do not belong in this repository.
2022-03-06 11:09:41 +01:00
hugoreleaser 9e76507da2 releaser: Prepare repository for 0.94.0-DEV
[ci skip]
2022-03-04 12:57:53 +00:00
hugoreleaser 643b5ae9cf releaser: Bump versions for release of 0.93.2
[ci skip]
2022-03-04 12:21:48 +00:00
hugoreleaser 2f7feca9a1 releaser: Add release notes for 0.93.2
[ci skip]
2022-03-04 12:21:48 +00:00
Bjørn Erik Pedersen 673cde1eb1 tpl/os: Revert readDir in theme behaviour
Fixes #9599
2022-03-04 08:43:47 +01:00
Joe Mooring e46e9ceb29 markup/goldmark: Escape image alt attribute
Fixes #9594
2022-03-02 21:05:33 +01:00
hugoreleaser 883e71c96a releaser: Prepare repository for 0.94.0-DEV
[ci skip]
2022-03-02 12:52:43 +00:00
hugoreleaser e9669fed14 releaser: Bump versions for release of 0.93.1
[ci skip]
2022-03-02 12:16:06 +00:00
hugoreleaser fefb1caacf releaser: Add release notes for 0.93.1
[ci skip]
2022-03-02 12:16:06 +00:00
Bjørn Erik Pedersen 0327da050f tpl/transform: Fix it when template.HTML is passes as option to Hightlight
Fixes #9591
2022-03-02 12:30:35 +01:00
Bjørn Erik Pedersen 9b8b6d34e2 tpl/partials: Fix partialCached deadlock regression
This is a rollback of  0927cf739f

We cannot do that change until we either completes #9570 or possibly also use the new TryLock in GO 1.18.

Fixes #9588
Opens #4086
2022-03-02 11:16:21 +01:00
Bjørn Erik Pedersen 376704d382 tpl/collections: Fix apply when function have Context as first arg
As introduced in `partial` and `partialCached` in Hugo 0.93.0.

Fixes #9585
2022-03-01 13:18:02 +01:00
Bjørn Erik Pedersen c1398b91a9 Squashed 'docs/' changes from 93f7baf80..3f95a2ace
3f95a2ace Update merge function examples (#1662)
e8d518011 requires -> require typo fix (#1666)
e714066cb Update GitLab deployment instructions (#1661)
11946a218 update GitLab CI/CD yaml syntax (#1649)
3568ddc03 Add explicit `contentDir` to module-config-mounts (#1658)
798ac8f68 Add default value to isHTML
d376565ce Create extensions.json

git-subtree-dir: docs
git-subtree-split: 3f95a2ace3
2022-02-28 10:37:27 +01:00
Bjørn Erik Pedersen 41b5bc9637 Merge commit 'c1398b91a9f4c67876b31feb67516b252e654d3c' 2022-02-28 10:37:27 +01:00
hugoreleaser 5a1b394f2a releaser: Prepare repository for 0.94.0-DEV
[ci skip]
2022-02-28 09:05:08 +00:00
hugoreleaser 0746908247 releaser: Bump versions for release of 0.93.0
[ci skip]
2022-02-28 08:30:42 +00:00
hugoreleaser 75084bfc72 releaser: Add release notes for 0.93.0
[ci skip]
2022-02-28 08:30:42 +00:00
Bjørn Erik Pedersen 12d00d288c docs: Regenerate docs helper 2022-02-28 08:59:57 +01:00
Bjørn Erik Pedersen 260ff1374d markup/highlight: Ignore HL_lines_parsed in the gen docs 2022-02-28 08:33:34 +01:00
Bjørn Erik Pedersen 14915a0c32 cod: Regen CLI docs 2022-02-28 08:12:35 +01:00
Bjørn Erik Pedersen fd0c1a5e9b tpl/diagrams: Rename the SVG accessor to Wrapped
Not perfect, but it gets it in line with the other .Inner/.Wrapped combos.
2022-02-27 19:51:40 +01:00
Bjørn Erik Pedersen 3ad39001df markup/highlight: Rework the return value from HighlightCodeblock
To make it possible to render it with a custom HTML ("<div>")  wrapper.

Updates #9573
2022-02-27 19:51:40 +01:00
Bjørn Erik Pedersen 39261b689e tpl/transform: Add CanHighlight
Closes #9573
2022-02-27 19:51:40 +01:00
Bjørn Erik Pedersen cff14144a6 Rename Codeowners() to CodeOwners() 2022-02-27 19:40:07 +01:00
Bjørn Erik Pedersen 5f67151550 tpl/diagrams: Rename Body to Inner
This relates to #9577 – there's no concept a body element in SVG, so we might as well use an exsisting "Hugo term".
2022-02-27 17:59:36 +01:00
Bjørn Erik Pedersen f7109771a0 CodeblockContext method renames
Fixes #9577
2022-02-27 17:59:36 +01:00
dependabot[bot] e1f696911e build(deps): bump github.com/tdewolff/minify/v2 from 2.9.29 to 2.10.0
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.29 to 2.10.0.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.29...v2.10.0)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-27 14:04:08 +01:00
Bjørn Erik Pedersen 5f65c17a12 markup/goldmark: Adjust test for Windows 2022-02-26 21:54:36 +01:00
Bjørn Erik Pedersen 579ff9b652 markup/goldmark: Improve attributes vs options
Fixes #9571
2022-02-26 21:54:36 +01:00
Bjørn Erik Pedersen 928a896962 markup/goldmark: Add Position to CodeblockContext
But note that this is not particulary fast and the recommendad usage is error logging only.

Updates #9574
2022-02-26 21:54:36 +01:00
Bjørn Erik Pedersen 2e54c00933 markup/goldmark: Unify some code block tests 2022-02-26 21:54:36 +01:00
Bjørn Erik Pedersen 10928a4f78 Remove the trailing new line in .Code
Fixes #9572
2022-02-26 21:54:36 +01:00
Bjørn Erik Pedersen afd63bf7d5 markup/goldmark: Rename extension struct 2022-02-26 21:54:36 +01:00
dependabot[bot] 228126b7f5 build(deps): bump github.com/gobuffalo/flect from 0.2.3 to 0.2.4
Bumps [github.com/gobuffalo/flect](https://github.com/gobuffalo/flect) from 0.2.3 to 0.2.4.
- [Release notes](https://github.com/gobuffalo/flect/releases)
- [Commits](https://github.com/gobuffalo/flect/compare/v0.2.3...v0.2.4)

---
updated-dependencies:
- dependency-name: github.com/gobuffalo/flect
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-26 10:24:59 +01:00
Bjørn Erik Pedersen 0f80be341f markup/goldmark: Use Ordinal to create default lineanchors
The `Ordinal` starts at 0, so with a `hl-` prefix, this gives `hl-0-1` as a starting point.

Fixes #9567
2022-02-25 17:22:43 +01:00
dependabot[bot] 78afdb88ab build(deps): bump github.com/gorilla/websocket from 1.4.2 to 1.5.0
Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.4.2 to 1.5.0.
- [Release notes](https://github.com/gorilla/websocket/releases)
- [Commits](https://github.com/gorilla/websocket/compare/v1.4.2...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/gorilla/websocket
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-25 08:18:20 +01:00
dependabot[bot] 3ed83227ba build(deps): bump github.com/sanity-io/litter from 1.5.1 to 1.5.2
Bumps [github.com/sanity-io/litter](https://github.com/sanity-io/litter) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/sanity-io/litter/releases)
- [Changelog](https://github.com/sanity-io/litter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sanity-io/litter/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/sanity-io/litter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-25 08:17:48 +01:00
Bjørn Erik Pedersen 1a257bb2ba Move the Goat template to the correct place
Updates #7765
2022-02-25 00:09:07 +01:00
dependabot[bot] 97514f17d6 build(deps): bump google.golang.org/api from 0.63.0 to 0.70.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.63.0 to 0.70.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.63.0...v0.70.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-24 23:05:09 +01:00
RheingoldRiver 0df096b868 Update error message about failed menus in config.toml 2022-02-24 23:04:06 +01:00
dependabot[bot] 308ad611b6 build(deps): bump github.com/getkin/kin-openapi from 0.85.0 to 0.90.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.85.0 to 0.90.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.85.0...v0.90.0)

---
updated-dependencies:
- dependency-name: github.com/getkin/kin-openapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-24 22:59:41 +01:00
Kaushal Modi 6bffcdbd26 Add test for line anchor attributes with code fences
Fixes https://github.com/gohugoio/hugo/issues/9385.
2022-02-24 22:54:49 +01:00
dependabot[bot] 7248f43188 build(deps): bump github.com/evanw/esbuild from 0.14.22 to 0.14.23
Bumps [github.com/evanw/esbuild](https://github.com/evanw/esbuild) from 0.14.22 to 0.14.23.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.14.22...v0.14.23)

---
updated-dependencies:
- dependency-name: github.com/evanw/esbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-24 21:30:30 +01:00
Bjørn Erik Pedersen 08fdca9d93 Add Markdown diagrams and render hooks for code blocks
You can now create custom hook templates for code blocks, either one for all (`render-codeblock.html`) or for a given code language (e.g. `render-codeblock-go.html`).

We also used this new hook to add support for diagrams in Hugo:

* Goat (Go ASCII Tool) is built-in and enabled by default; just create a fenced code block with the language `goat` and start draw your Ascii diagrams.
* Another popular alternative for diagrams in Markdown, Mermaid (supported by GitHub), can also be implemented with a simple template. See the Hugo documentation for more information.

Updates #7765
Closes #9538
Fixes #9553
Fixes #8520
Fixes #6702
Fixes #9558
2022-02-24 18:59:50 +01:00
dependabot[bot] 2c20f5bc00 build(deps): bump github.com/aws/aws-sdk-go from 1.41.14 to 1.43.5
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.41.14 to 1.43.5.
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.41.14...v1.43.5)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-24 17:25:24 +01:00
dependabot[bot] 723b2c485b build(deps): bump github.com/google/go-cmp from 0.5.6 to 0.5.7
Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.5.6 to 0.5.7.
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.5.6...v0.5.7)

---
updated-dependencies:
- dependency-name: github.com/google/go-cmp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-24 17:24:46 +01:00
Marshall Cottrell 06bac57ab0 Add support for CODEOWNERS
Fixes #9474
2022-02-23 22:43:19 +01:00
Bjørn Erik Pedersen ec8b767fa6 Remove Viper as a dependency 2022-02-23 22:40:23 +01:00
Cameron Moore 6407b2cd01 helpers: Allow hyphens in UnicodeSanitize
Improve handling of existing hyphens in input to UnicodeSanitize.
This commit accomplishes three things:

1. Explicitly allow hyphens
2. Avoid appending a hyphen if a preceeding hyphen is found
3. Avoid prepending a hyphen if a trailing hyphen is found

Fixes #7288
2022-02-23 21:21:46 +01:00
Akimo 6ff39fd908 Change disqus_config to window.disqus_config 2022-02-23 21:19:30 +01:00
Anthony Fok 96c0bdf3aa deps: Update github.com/spf13/cobra v1.2.1 => v1.3.0 2022-02-23 20:53:51 +01:00
Bjørn Erik Pedersen e97d3c640f Add page.Store
Fixes #9546
2022-02-23 10:02:16 +01:00
John Elliott 7732da9f93 Allow images to be cropped without being resized
Introduces the Crop method for image processing which implements gift.CropToSize. Also allows a smartCrop without resizing, and updates the documentation.

Fixes #9499
2022-02-23 10:01:50 +01:00
Bjørn Erik Pedersen aebde49b88 commands: Fix server panic regression
And now with a proper server test.

Fixes #9518
Fixes #9530
Fixes #9539
2022-02-21 21:43:26 +01:00
Bjørn Erik Pedersen 4ada09415d markup/goldmark: Add BenchmarkCodeblocks 2022-02-21 20:53:52 +01:00
Joe Mooring bddcfd911c deps: Update github.com/gohugoio/localescompressed v0.14.0 => v0.15.0
Fixes #9446
2022-02-19 11:34:00 +01:00
Bjørn Erik Pedersen d485f95431 deps: Update github.com/yuin/goldmark v1.4.4 => v1.4.7
Closes #9397
2022-02-19 11:30:16 +01:00
Bjørn Erik Pedersen a87be597af modules: Add modules.Workspace config for Go 1.18
Sets `GOWORK` env var for Go 1.18.

Fixes #9525
2022-02-18 15:06:58 +01:00
Bjørn Erik Pedersen b0eea00756 Update stale.yml
Updates #9522
2022-02-18 11:43:35 +01:00
Bjørn Erik Pedersen ef8b781b14 Update stale.yml
Updates #9522
2022-02-18 11:38:24 +01:00
Bjørn Erik Pedersen 3136ff67d8 github: Configure stale to be run manually
Updates #9522
2022-02-18 11:31:57 +01:00
Bjørn Erik Pedersen 929808190f tpl/partials: Fix recently introduced deadlock in partials cache
The change in lock logic for `partialCached` in  0927cf739f was naive as it didn't consider cached partials calling other cached partials.

This changeset may look on the large side for this particular issue, but it pulls in part of a working branch, introducing `context.Context` in the template execution.

Note that the context is only partially implemented in this PR, but the upcoming use cases will, as one example, include having access to the top "dot" (e.g. `Page`) all the way down into partials and shortcodes etc.

The earlier benchmarks rerun against master:

```bash
name              old time/op    new time/op    delta
IncludeCached-10    13.6ms ± 2%    13.8ms ± 1%    ~     (p=0.343 n=4+4)

name              old alloc/op   new alloc/op   delta
IncludeCached-10    5.30MB ± 0%    5.35MB ± 0%  +0.96%  (p=0.029 n=4+4)

name              old allocs/op  new allocs/op  delta
IncludeCached-10     74.7k ± 0%     75.3k ± 0%  +0.77%  (p=0.029 n=4+4)
```

Fixes #9519
2022-02-17 18:47:36 +01:00
Bjørn Erik Pedersen 667f3a4ba8 tpl/partials: Add some more partials to BenchmarkIncludeCached 2022-02-17 16:45:59 +01:00
Bjørn Erik Pedersen c061b253a4 deps: Update github.com/evanw/esbuild v0.14.11 => v0.14.22 2022-02-17 12:34:13 +01:00
Bjørn Erik Pedersen 0927cf739f tpl/partials: Make sure a cached partial is invoked only once
This commit revises the locking strategy for `partialCached`. We have added a benchmark that may be a little artificial, but it should at least show that we're not losing any performance over this:

```bash
name              old time/op    new time/op    delta
IncludeCached-10    12.2ms ± 2%    11.3ms ± 1%   -7.36%  (p=0.029 n=4+4)

name              old alloc/op   new alloc/op   delta
IncludeCached-10    7.17MB ± 0%    5.09MB ± 0%  -29.00%  (p=0.029 n=4+4)

name              old allocs/op  new allocs/op  delta
IncludeCached-10      128k ± 1%       70k ± 0%  -45.42%  (p=0.029 n=4+4)
```

This commit also revises the template metrics hints logic a little, and add a test for it, which output is currently this:

```bash
 cumulative       average       maximum      cache  percent  cached  total
       duration      duration      duration  potential   cached   count  count  template
     ----------      --------      --------  ---------  -------  ------  -----  --------
      163.334µs     163.334µs     163.334µs          0        0       0      1  index.html
       23.749µs       5.937µs      19.916µs         25       50       2      4  partials/dynamic1.html
        9.625µs       4.812µs        6.75µs        100       50       1      2  partials/static1.html
        7.625µs       7.625µs       7.625µs        100        0       0      1  partials/static2.html
```

Some notes:

* The duration now includes the cached invocations (which should be very short)
* A cached template gets executed once before it gets cached, so the "percent cached" will never be 100.

Fixes #4086
Fixes #9506
2022-02-17 11:53:24 +01:00
dependabot[bot] 26a5e89faa build(deps): bump github.com/rogpeppe/go-internal from 1.8.0 to 1.8.1
Bumps [github.com/rogpeppe/go-internal](https://github.com/rogpeppe/go-internal) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/rogpeppe/go-internal/releases)
- [Commits](https://github.com/rogpeppe/go-internal/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/rogpeppe/go-internal
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-17 09:55:06 +01:00
dependabot[bot] e9fa7e81b3 build(deps): bump github.com/magefile/mage from 1.11.0 to 1.12.1
Bumps [github.com/magefile/mage](https://github.com/magefile/mage) from 1.11.0 to 1.12.1.
- [Release notes](https://github.com/magefile/mage/releases)
- [Changelog](https://github.com/magefile/mage/blob/master/.goreleaser.yml)
- [Commits](https://github.com/magefile/mage/compare/v1.11.0...v1.12.1)

---
updated-dependencies:
- dependency-name: github.com/magefile/mage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-17 09:54:29 +01:00
Joe Mooring ff545f4276 markup/goldmark: Exclude event attributes from markdown render hook
Fixes #9511
2022-02-16 19:56:23 +01:00
Bjørn Erik Pedersen b2a827c52c markup/goldmark: Fix mangling of headers/links in render hooks
```bash

name                    old time/op    new time/op    delta
SiteWithRenderHooks-10    11.9ms ± 1%    11.9ms ± 1%    ~     (p=0.486 n=4+4)

name                    old alloc/op   new alloc/op   delta
SiteWithRenderHooks-10    11.2MB ± 0%    11.3MB ± 0%  +0.16%  (p=0.029 n=4+4)

name                    old allocs/op  new allocs/op  delta
SiteWithRenderHooks-10      145k ± 0%      145k ± 0%  +0.14%  (p=0.029 n=4+4)
```

Fixes #9504
2022-02-16 17:46:30 +01:00
Bjørn Erik Pedersen 77c7059ff8 markup/goldmark: Add a render hook benchmark
Updates #9504
2022-02-16 13:30:53 +01:00
Bjørn Erik Pedersen f4c90bd6ba Fix BenchmarkCascadeTarget 2022-02-16 13:23:43 +01:00
Cameron Moore d16228334d metrics: Add cached count tracking
Track cached partial executions and display more useful info when
showing the hints output. Also group cache hints and counters together
to the right of the timing measurements.

Sample output:

     cumulative       average       maximum      cache  percent  cached  total
       duration      duration      duration  potential   cached   count  count  template
     ----------      --------      --------  ---------  -------  ------  -----  --------
       2.6973ms     674.325µs       857.3µs          0        0       0      4  _internal/_default/rss.xml
       2.6295ms     657.375µs       861.3µs          0        0       0      4  _default/single.html
       1.2563ms     314.075µs       381.5µs          0        0       0      4  news/single.html
        799.2µs       399.6µs       573.3µs          0        0       0      2  _internal/_default/sitemap.xml
        667.3µs       66.73µs       238.6µs         84        0       0     10  partials/header.html
        656.7µs      328.35µs       459.4µs          0        0       0      2  _default/list.html
        479.7µs       47.97µs         158µs         92        0       0     10  partials/head.html
        323.3µs       323.3µs       323.3µs          0        0       0      1  _internal/alias.html
        200.7µs       200.7µs       200.7µs          0        0       0      1  _internal/_default/sitemapindex.xml
         36.1µs       6.016µs        32.6µs        100       33       2      6  partials/footer.html
         27.4µs        13.7µs        26.7µs          0        0       0      2  index.html

Fixes #8375
2022-02-16 10:05:17 +01:00
Bjørn Erik Pedersen ea6bcd6944 tpl: Remove TODO comment 2022-02-15 23:39:54 +01:00
Bjørn Erik Pedersen f2e7b49acf Add --printUnusedTemplates
Fixes #9502
2022-02-15 20:01:57 +01:00
Joe Mooring 923419d7fd deps: Update github.com/tdewolff/minify/v2 v2.9.22 => v2.9.29
Fixes #9244
Fixes #9132
Fixes https://discourse.gohugo.io/t/36523
2022-02-15 17:36:29 +01:00
Bjørn Erik Pedersen 837fdfdf45 commands: Rename --i18n-warnings to printI18nWarnings
To get it in line with the others.
2022-02-15 13:03:36 +01:00
Bjørn Erik Pedersen 6819feab6c commands: Rename --path-warnings, --print-men to --printPathWarnings, --printMemoryUsage
To get it in line with the others.
2022-02-15 13:00:06 +01:00
Bjørn Erik Pedersen ea54a99ca5 deps: Update github.com/alecthomas/chroma v0.9.4 => v0.10.0 2022-02-15 10:37:14 +01:00
Bjørn Erik Pedersen 9563c7d13b Finally remove deprecated Page methods
They have been deprecated for a very long time, first with a warning, then with an ERROR. Now they are removed.

Closes #4117
2022-02-14 13:24:36 +01:00
Bjørn Erik Pedersen 230a495941 Squashed 'docs/' changes from b8b20e9a2..93f7baf80
93f7baf80 netlify: Hugo 0.92.2
ecd8a6b6b Merge branch 'tempv0.92.2'
ee04caf9f Fix validation of Page Kind in cascade target map
3fce03ed2 Remove the "check" command
70ad71b25 Fix typo (#1651)
dfd1dc570 Fix internal link (#1650)
b5e0e0d38 Update rotten Fedora package link
78276390a Add missing word in types.md (#1645)
45eb945f8 Remove hugo gen autocomplete
2f84d0896 netlify: Hugo 0.92.1
b55f5773b Update complement.md
7013ee43e Remove deprecated RSSLink from page variables
1c3fb6ed3 Remove deprecated page vars (#1638)
9bd0af3ab Update union.md
4576487a0 Update symdiff.md
58f1b26c7 Update starter-kits.md (#1521)
285356c1d Move inline partials section (#1635)
4d08625d2 Improve directions for editing Win 10 path env var (#1632)
41d9c7804 Update page content (#1629)
d05536cad Removed unpaired quote character (#1630)
3b0fdcd88 netlify: Bump to Hugo 0.92.0
cdb068346 docs: Regenerate docshelper
778503ca8 Merge commit 'a8e9fc699a6ff7d578f97a7c553ce844efad8fdb'
2d2401c91 docs: Add dependency table to maintainance page
6bc95c386 Add hugo.Deps
19d0b5e23 docs. Regen CLI docs
76a750d96 docs: Regenerate CLI docs
70156962a docs: Regenerate docshelper

git-subtree-dir: docs
git-subtree-split: 93f7baf80b
2022-02-14 12:58:42 +01:00
Bjørn Erik Pedersen c707b71cdf Merge commit '230a495941b191af0bdaa7e2fc8c61607cb38207' 2022-02-14 12:58:42 +01:00
Bjørn Erik Pedersen b0a9cf0a75 tpl: Use go:embed to load internal templates
Fixes #8297
2022-02-14 09:39:46 +01:00
hugoreleaser 9433cc2562 releaser: Prepare repository for 0.93.0-DEV
[ci skip]
2022-02-11 14:54:18 +00:00
865 changed files with 19331 additions and 20572 deletions
+96 -33
View File
@@ -1,51 +1,114 @@
defaults: &defaults
docker:
- image: bepsays/ci-goreleaser:1.1700.600
environment:
CGO_ENABLED: "0"
parameters:
# v2: 11m.
defaults: &defaults
resource_class: large
docker:
- image: bepsays/ci-hugoreleaser:1.21900.20003
environment: &buildenv
GOMODCACHE: /root/project/gomodcache
version: 2
jobs:
build:
<<: *defaults
prepare_release:
<<: *defaults
environment: &buildenv
GOMODCACHE: /root/project/gomodcache
steps:
- checkout:
- &remote-docker
setup_remote_docker:
version: 20.10.14
- checkout:
path: hugo
- &git-config
run:
command: |
git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com"
git config --global user.name "hugoreleaser"
- run:
command: |
git clone git@github.com:gohugoio/hugoDocs.git
cd hugo
go mod download
sleep 5
go mod verify
- persist_to_workspace:
root: .
paths: .
release:
<<: *defaults
command: |
cd hugo
go mod download
go run -tags release main.go release --step 1
- save_cache:
key: git-sha-{{ .Revision }}
paths:
- hugo
- gomodcache
build_container1:
<<: [*defaults]
environment:
<<: [*buildenv]
steps:
- attach_workspace:
at: /root/project
- &restore-cache
restore_cache:
key: git-sha-{{ .Revision }}
- run:
command: |
cd hugo
git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com"
git config --global user.name "hugoreleaser"
go run -tags release main.go release -r ${CIRCLE_BRANCH}
no_output_timeout: 20m
command: |
mkdir -p /tmp/files/dist1
cd hugo
hugoreleaser build -paths "builds/container1/**" -workers 3 -dist /tmp/files/dist1 -chunks $CIRCLE_NODE_TOTAL -chunk-index $CIRCLE_NODE_INDEX
- &persist-workspace
persist_to_workspace:
root: /tmp/files
paths:
- dist1
- dist2
parallelism: 7
build_container2:
<<: [*defaults]
environment:
<<: [*buildenv]
docker:
- image: bepsays/ci-hugoreleaser-linux-arm64:1.21900.20003
steps:
- *restore-cache
- &attach-workspace
attach_workspace:
at: /tmp/workspace
- run:
command: |
mkdir -p /tmp/files/dist2
cd hugo
hugoreleaser build -paths "builds/container2/**" -workers 1 -dist /tmp/files/dist2
- *persist-workspace
archive_and_release:
<<: [*defaults]
environment:
<<: [*buildenv]
steps:
- *restore-cache
- *attach-workspace
- *git-config
- run:
command: |
cp -a /tmp/workspace/dist1/. ./hugo/dist
cp -a /tmp/workspace/dist2/. ./hugo/dist
- run:
command: |
cd hugo
hugoreleaser archive
hugoreleaser release
go run -tags release main.go release --step 2
workflows:
version: 2
release:
jobs:
- build:
- prepare_release:
filters:
branches:
only: /release-.*/
- hold:
type: approval
- build_container1:
requires:
- build
- release:
- prepare_release
- build_container2:
requires:
- prepare_release
- archive_and_release:
context: org-global
requires:
- hold
- build_container1
- build_container2
+12 -5
View File
@@ -1,14 +1,20 @@
name: 'Close stale and lock closed issues and PRs'
on:
workflow_dispatch:
schedule:
- cron: '30 1 * * *'
permissions:
contents: read
jobs:
stale:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@08e671be8ac8944d0e132aa71d0ae8ccfb347675
with:
issue-inactive-days: 7
issue-inactive-days: 21
add-issue-labels: 'Outdated'
issue-comment: >
This issue has been automatically locked since there
@@ -20,10 +26,11 @@ jobs:
Please open a new issue for related bugs.
- uses: actions/stale@04a1828bc18ada028d85a0252a47cd2963a91abe
with:
days-before-issue-stale: 600
days-before-pr-stale: 200
days-before-issue-close: -1
days-before-pr-close: -1
operations-per-run: 999
days-before-issue-stale: 365
days-before-pr-stale: 365
days-before-issue-close: 56
days-before-pr-close: 56
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
recent activity. The resources of the Hugo team are limited, and so we are asking for your help.
+15 -5
View File
@@ -1,5 +1,7 @@
on: [push, pull_request]
name: Test
permissions:
contents: read
jobs:
test:
env:
@@ -7,7 +9,11 @@ jobs:
GO111MODULE: on
strategy:
matrix:
go-version: [1.16.x, 1.17.x]
# Note: We upgraded to Go 1.18 in Hugo v0.95.0
# Go 1.18 had some breaking changes on the source level which means Hugo cannot be built
# with older Go versions, but the improvements in Go 1.18 were too good to pass on (e.g. break and continue).
# Note that you don't need Go (or Go 1.18) to run a pre-built binary.
go-version: [1.18.x,1.19.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
@@ -24,7 +30,7 @@ jobs:
with:
python-version: '3.x'
- name: Install Mage
run: go get github.com/magefile/mage@07afc7d24f4d6d6442305d49552f04fbda5ccb3e
run: go install github.com/magefile/mage@07afc7d24f4d6d6442305d49552f04fbda5ccb3e
- name: Install asciidoctor
uses: reitzig/actions-asciidoctor@7570212ae20b63653481675fb1ff62d1073632b0
- name: Checkout code
@@ -43,8 +49,11 @@ jobs:
brew install pandoc
- if: matrix.os == 'windows-latest'
run: |
choco install pandoc
Choco-Install -PackageName pandoc
- run: pandoc -v
- if: matrix.os == 'windows-latest'
run: |
Choco-Install -PackageName mingw -ArgumentList "--version","10.2.0","--allow-downgrade"
- if: matrix.os == 'ubuntu-latest'
name: Install dart-sass-embedded Linux
run: |
@@ -66,10 +75,11 @@ jobs:
echo "6ae442129dbb3334bc21ef851261da6c0c1b560da790ca2e1350871d00ab816d sass_embedded-1.0.0-beta.6-windows-x64.zip" | sha256sum -c;
unzip sass_embedded-1.0.0-beta.6-windows-x64.zip;
echo "$env:GITHUB_WORKSPACE/sass_embedded/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append
- name: Test
- name: Check
run: |
mage -v test
mage -v check;
env:
HUGO_BUILD_TAGS: extended
- name: Build Docs
env:
HUGO_BUILD_TAGS: extended
+14 -9
View File
@@ -1,5 +1,7 @@
# Contributing to Hugo
**Note March 16th 2022:** We are currently very constrained on human resources to do code reviews, so we currently require any new Pull Requests to be limited to bug fixes closing an existing issue. Also, we have updated to Go 1.18, but we will currently not accept any generic rewrites, "interface{} to any" replacements and similar.
We welcome contributions to Hugo of any kind including documentation, themes,
organization, tutorials, blog posts, bug reports, issues, feature requests,
feature implementations, pull requests, answering questions on the forum,
@@ -81,19 +83,22 @@ To make the contribution process as seamless as possible, we ask for the followi
### Git Commit Message Guidelines
This [blog article](http://chris.beams.io/posts/git-commit/) is a good resource for learning how to write good commit messages,
This [blog article](https://cbea.ms/git-commit/) is a good resource for learning how to write good commit messages,
the most important part being that each commit message should have a title/subject in imperative mood starting with a capital letter and no trailing period:
*"Return error on wrong use of the Paginator"*, **NOT** *"returning some error."*
*"js: Return error when option x is not set"*, **NOT** *"returning some error."*
Most title/subjects should have a lower-cased prefix with a colon and one whitespace. The prefix can be:
* The name of the package where (most of) the changes are made (e.g. `media: Add text/calendar`)
* If the package name is deeply nested/long, try to shorten it from the left side, e.g. `markup/goldmark` is OK, `resources/resource_transformers/js` can be shortened to `js`.
* If this commit touches several packages with a common functional topic, use that as a prefix, e.g. `errors: Resolve correct line numbers`)
* If this commit touches many packages without a common functional topic, prefix with `all:` (e.g. `all: Reformat Go code`)
* If this is a documentation update, prefix with `docs:`.
* If nothing of the above applies, just leave the prefix out.
Also, if your commit references one or more GitHub issues, always end your commit message body with *See #1234* or *Fixes #1234*.
Replace *1234* with the GitHub issue ID. The last example will close the issue when the commit is merged into *master*.
Sometimes it makes sense to prefix the commit message with the package name (or docs folder) all lowercased ending with a colon.
That is fine, but the rest of the rules above apply.
So it is "tpl: Add emojify template func", not "tpl: add emojify template func.", and "docs: Document emoji", not "doc: document emoji."
Please use a short and descriptive branch name, e.g. **NOT** "patch-1". It's very common but creates a naming conflict each time when a submission is pulled for a review.
An example:
```text
@@ -124,7 +129,7 @@ go install
For some convenient build and test targets, you also will want to install Mage:
```bash
go get github.com/magefile/mage
go install github.com/magefile/mage
```
Now, to make a change to Hugo's source:
+2 -2
View File
@@ -2,7 +2,7 @@
# Twitter: https://twitter.com/gohugoio
# Website: https://gohugo.io/
FROM golang:1.16-alpine AS build
FROM golang:1.18-alpine AS build
# Optionally set HUGO_BUILD_TAGS to "extended" or "nodeploy" when building like so:
# docker build --build-arg HUGO_BUILD_TAGS=extended .
@@ -20,7 +20,7 @@ COPY . /go/src/github.com/gohugoio/hugo/
# gcc/g++ are required to build SASS libraries for extended version
RUN apk update && \
apk add --no-cache gcc g++ musl-dev git && \
go get github.com/magefile/mage
go install github.com/magefile/mage
RUN mage hugo && mage install
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2022 The Hugo Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+102 -76
View File
@@ -1,6 +1,6 @@
<a href="https://gohugo.io/"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/hugo-logo-wide.svg?sanitize=true" alt="Hugo" width="565"></a>
A Fast and Flexible Static Site Generator built with love by [bep](https://github.com/bep), [spf13](http://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go][].
A Fast and Flexible Static Site Generator built with love by [bep](https://github.com/bep), [spf13](https://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go](https://go.dev/).
[Website](https://gohugo.io) |
[Forum](https://discourse.gohugo.io) |
@@ -13,9 +13,19 @@ A Fast and Flexible Static Site Generator built with love by [bep](https://githu
[![Tests on Linux, MacOS and Windows](https://github.com/gohugoio/hugo/workflows/Test/badge.svg)](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
[![Go Report Card](https://goreportcard.com/badge/github.com/gohugoio/hugo)](https://goreportcard.com/report/github.com/gohugoio/hugo)
* [Overview](#overview)
* [Banner Sponsors](#banner-sponsors)
* [Supported Architectures](#supported-architectures)
* [Choose How to Install](#choose-how-to-install)
* [Install Hugo as Your Site Generator (Binary Install)](#install-hugo-as-your-site-generator-binary-install)
* [Build and Install the Binaries from Source (Advanced Install)](#build-and-install-the-binaries-from-source-advanced-install)
* [The Hugo Documentation](#the-hugo-documentation)
* [Contributing to Hugo](#contributing-code-to-hugo)
* [Dependencies](#dependencies)
## Overview
Hugo is a static HTML and CSS website generator written in [Go][].
Hugo is a static HTML and CSS website generator written in [Go](https://go.dev/).
It is optimized for speed, ease of use, and configurability.
Hugo takes a directory with content and templates and renders them into a full HTML website.
@@ -27,7 +37,16 @@ A good rule of thumb is that each piece of content renders in around 1 milliseco
Hugo is designed to work well for any kind of website including blogs, tumbles, and docs.
#### Supported Architectures
## Banner Sponsors
<p>&nbsp;</p>
<p float="left">
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://esolia.com/post/why-did-esolia-choose-hugo/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/sponsors/esolia-logo.svg?sanitize=true" width="200" alt="eSOLIA"></a>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://buttercms.com/hugo-cms/?utm_campaign=sponsorship&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/sponsors/butter-dark.svg?sanitize=true" width="280" alt="ButterCMS"></a>
</p>
<p>&nbsp;</p>
## Supported Architectures
Currently, we provide pre-built Hugo binaries for Windows, Linux, FreeBSD, NetBSD, DragonFly BSD, OpenBSD, macOS (Darwin), and [Android](https://gist.github.com/bep/a0d8a26cf6b4f8bc992729b8e50b480b) for x64, i386 and ARM architectures.
@@ -38,7 +57,6 @@ Hugo may also be compiled from source wherever the Go compiler tool chain can ru
## Choose How to Install
If you want to use Hugo as your site generator, simply install the Hugo binaries.
The Hugo binaries have no external dependencies.
To contribute to the Hugo source code or documentation, you should [fork the Hugo GitHub project](https://github.com/gohugoio/hugo#fork-destination-box) and clone it to your local machine.
@@ -54,11 +72,11 @@ Use the [installation instructions in the Hugo documentation](https://gohugo.io/
#### Prerequisite Tools
* [Git](https://git-scm.com/)
* [Go (we test it with the last 2 major versions; but note that Hugo 0.81.0 only builds with >= Go 1.16.)](https://golang.org/dl/)
* [Go (we test it with the last 2 major versions; but note that Hugo 0.95.0 only builds with >= Go 1.18.)](https://golang.org/dl/)
#### Fetch from GitHub
Since Hugo 0.48, Hugo uses the Go Modules support built into Go 1.11 to build. The easiest is to clone Hugo in a directory outside of `GOPATH`, as in the following example:
To fetch and build the source from GitHub:
```bash
mkdir $HOME/src
@@ -83,7 +101,9 @@ The Hugo documentation now lives in its own repository, see https://github.com/g
```bash
git clone git@github.com:gohugoio/hugo.git
```
## Contributing to Hugo
## Contributing code to Hugo
**Note March 16th 2022:** We are currently very constrained on human resources to do code reviews, so we currently require any new Pull Requests to be limited to bug fixes closing an existing issue. Also, we have updated to Go 1.18, but we will currently not accept any generic rewrites, "interface{} to any" replacements and similar.
For a complete guide to contributing to Hugo, see the [Contribution Guide](CONTRIBUTING.md).
@@ -94,131 +114,137 @@ helping to manage issues, etc.
The Hugo community and maintainers are [very active](https://github.com/gohugoio/hugo/pulse/monthly) and helpful, and the project benefits greatly from this activity.
### 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.
### Reporting Issues
## Reporting Issues
If you believe you have found a defect in Hugo or its documentation, use
the GitHub issue tracker to report the problem to the Hugo maintainers.
If you're not sure if it's a bug or not, start by asking in the [discussion forum](https://discourse.gohugo.io).
When reporting the issue, please provide the version of Hugo in use (`hugo version`).
### Submitting Patches
The Hugo project welcomes all contributors and contributions regardless of skill or experience level.
If you are interested in helping with the project, we will help you with your contribution.
Hugo is a very active project with many contributions happening daily.
We want to create the best possible product for our users and the best contribution experience for our developers,
we have a set of guidelines which ensure that all contributions are acceptable.
The guidelines are not intended as a filter or barrier to participation.
If you are unfamiliar with the contribution process, the Hugo team will help you and teach you how to bring your contribution in accordance with the guidelines.
For a complete guide to contributing code to Hugo, see the [Contribution Guide](CONTRIBUTING.md).
[Go]: https://golang.org/
[Hugo Documentation]: https://gohugo.io/overview/introduction/
## Dependencies
Hugo stands on the shoulder of many great open source libraries.
If you run `hugo env -v` you will get a complete and up to date list.
In Hugo 0.89.0 that list is, in lexical order:
In Hugo 0.100.1 that list is, in lexical order:
```
cloud.google.com/go/storage="v1.10.0"
cloud.google.com/go="v0.87.0"
github.com/Azure/azure-pipeline-go="v0.2.2"
github.com/Azure/azure-storage-blob-go="v0.9.0"
cloud.google.com/go/compute="v1.6.1"
cloud.google.com/go/iam="v0.3.0"
cloud.google.com/go/storage="v1.22.0"
cloud.google.com/go="v0.101.0"
github.com/Azure/azure-pipeline-go="v0.2.3"
github.com/Azure/azure-storage-blob-go="v0.14.0"
github.com/Azure/go-autorest/autorest/adal="v0.9.15"
github.com/Azure/go-autorest/autorest/date="v0.3.0"
github.com/Azure/go-autorest/autorest="v0.11.20"
github.com/Azure/go-autorest/logger="v0.2.1"
github.com/Azure/go-autorest/tracing="v0.6.0"
github.com/BurntSushi/locker="v0.0.0-20171006230638-a6e239ea1c69"
github.com/BurntSushi/toml="v0.3.1"
github.com/PuerkitoBio/purell="v1.1.1"
github.com/PuerkitoBio/urlesc="v0.0.0-20170810143723-de5bf2ad4578"
github.com/alecthomas/chroma="v0.9.4"
github.com/alecthomas/chroma="v0.10.0"
github.com/armon/go-radix="v1.0.0"
github.com/aws/aws-sdk-go="v1.41.14"
github.com/aws/aws-sdk-go-v2/config="v1.7.0"
github.com/aws/aws-sdk-go-v2/credentials="v1.4.0"
github.com/aws/aws-sdk-go-v2/feature/ec2/imds="v1.5.0"
github.com/aws/aws-sdk-go-v2/internal/ini="v1.2.2"
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url="v1.3.0"
github.com/aws/aws-sdk-go-v2/service/sso="v1.4.0"
github.com/aws/aws-sdk-go-v2/service/sts="v1.7.0"
github.com/aws/aws-sdk-go-v2="v1.9.0"
github.com/aws/aws-sdk-go="v1.43.5"
github.com/aws/smithy-go="v1.8.0"
github.com/bep/clock="v0.3.0"
github.com/bep/debounce="v1.2.0"
github.com/bep/gitmap="v1.1.2"
github.com/bep/godartsass="v0.12.0"
github.com/bep/golibsass="v1.0.0"
github.com/bep/goat="v0.5.0"
github.com/bep/godartsass="v0.14.0"
github.com/bep/golibsass="v1.1.0"
github.com/bep/gowebp="v0.1.0"
github.com/bep/overlayfs="v0.6.0"
github.com/bep/tmc="v0.5.1"
github.com/clbanning/mxj/v2="v2.5.5"
github.com/cli/safeexec="v1.0.0"
github.com/cpuguy83/go-md2man/v2="v2.0.0"
github.com/cpuguy83/go-md2man/v2="v2.0.1"
github.com/disintegration/gift="v1.2.1"
github.com/dlclark/regexp2="v1.4.0"
github.com/dustin/go-humanize="v1.0.0"
github.com/evanw/esbuild="v0.13.12"
github.com/fsnotify/fsnotify="v1.5.1"
github.com/getkin/kin-openapi="v0.80.0"
github.com/evanw/esbuild="v0.14.42"
github.com/frankban/quicktest="v1.14.3"
github.com/fsnotify/fsnotify="v1.5.4"
github.com/getkin/kin-openapi="v0.94.0"
github.com/ghodss/yaml="v1.0.0"
github.com/go-openapi/jsonpointer="v0.19.5"
github.com/go-openapi/swag="v0.19.5"
github.com/gobuffalo/flect="v0.2.3"
github.com/gobuffalo/flect="v0.2.5"
github.com/gobwas/glob="v0.2.3"
github.com/gohugoio/go-i18n/v2="v2.1.3-0.20210430103248-4c28c89f8013"
github.com/gohugoio/locales="v0.14.0"
github.com/gohugoio/localescompressed="v0.14.0"
github.com/golang/groupcache="v0.0.0-20200121045136-8c9f03a8e57e"
github.com/gohugoio/localescompressed="v1.0.1"
github.com/golang-jwt/jwt/v4="v4.0.0"
github.com/golang/groupcache="v0.0.0-20210331224755-41bb18bfe9da"
github.com/golang/protobuf="v1.5.2"
github.com/google/go-cmp="v0.5.6"
github.com/google/uuid="v1.1.2"
github.com/google/wire="v0.4.0"
github.com/googleapis/gax-go/v2="v2.0.5"
github.com/googleapis/gax-go="v2.0.2+incompatible"
github.com/gorilla/websocket="v1.4.2"
github.com/google/go-cmp="v0.5.8"
github.com/google/uuid="v1.3.0"
github.com/google/wire="v0.5.0"
github.com/googleapis/gax-go/v2="v2.3.0"
github.com/googleapis/go-type-adapters="v1.0.0"
github.com/gorilla/websocket="v1.5.0"
github.com/hairyhenderson/go-codeowners="v0.2.3-0.20201026200250-cdc7c0759690"
github.com/inconshreveable/mousetrap="v1.0.0"
github.com/jdkato/prose="v1.2.1"
github.com/jmespath/go-jmespath="v0.4.0"
github.com/kyokomi/emoji/v2="v2.2.8"
github.com/kr/pretty="v0.3.0"
github.com/kr/text="v0.2.0"
github.com/kyokomi/emoji/v2="v2.2.9"
github.com/mailru/easyjson="v0.0.0-20190626092158-b2ccc519800e"
github.com/mattn/go-ieproxy="v0.0.1"
github.com/mattn/go-isatty="v0.0.14"
github.com/mattn/go-runewidth="v0.0.9"
github.com/miekg/mmark="v1.3.6"
github.com/mitchellh/hashstructure="v1.1.0"
github.com/mitchellh/mapstructure="v1.4.2"
github.com/mitchellh/mapstructure="v1.5.0"
github.com/muesli/smartcrop="v0.3.0"
github.com/niklasfasching/go-org="v1.5.0"
github.com/niklasfasching/go-org="v1.6.2"
github.com/olekukonko/tablewriter="v0.0.5"
github.com/pelletier/go-toml/v2="v2.0.0-beta.3.0.20210727221244-fa0796069526"
github.com/pkg/errors="v0.9.1"
github.com/rogpeppe/go-internal="v1.8.0"
github.com/russross/blackfriday/v2="v2.0.1"
github.com/russross/blackfriday="v1.5.3-0.20200218234912-41c5fccfd6f6"
github.com/pelletier/go-toml/v2="v2.0.0-beta.7.0.20220408132554-2377ac4bc04c"
github.com/rogpeppe/go-internal="v1.8.1"
github.com/russross/blackfriday/v2="v2.1.0"
github.com/rwcarlsen/goexif="v0.0.0-20190401172101-9e8deecbddbd"
github.com/sanity-io/litter="v1.5.1"
github.com/sanity-io/litter="v1.5.5"
github.com/sass/libsass="3.6.5"
github.com/shurcooL/sanitized_anchor_name="v1.0.0"
github.com/spf13/afero="v1.6.0"
github.com/spf13/cast="v1.4.1"
github.com/spf13/cobra="v1.2.1"
github.com/spf13/afero="v1.8.2"
github.com/spf13/cast="v1.5.0"
github.com/spf13/cobra="v1.4.0"
github.com/spf13/fsync="v0.9.0"
github.com/spf13/jwalterweatherman="v1.1.0"
github.com/spf13/pflag="v1.0.5"
github.com/tdewolff/minify/v2="v2.9.22"
github.com/tdewolff/parse/v2="v2.5.21"
github.com/tdewolff/minify/v2="v2.11.5"
github.com/tdewolff/parse/v2="v2.5.31"
github.com/webmproject/libwebp="v1.2.0"
github.com/yuin/goldmark-highlighting="v0.0.0-20200307114337-60d527fdb691"
github.com/yuin/goldmark="v1.4.2"
github.com/yuin/goldmark="v1.4.12"
go.opencensus.io="v0.23.0"
gocloud.dev="v0.20.0"
golang.org/x/image="v0.0.0-20210220032944-ac19c3e999fb"
golang.org/x/net="v0.0.0-20210614182718-04defd469f4e"
golang.org/x/oauth2="v0.0.0-20210628180205-a41e5a781914"
go.uber.org/atomic="v1.9.0"
gocloud.dev="v0.24.0"
golang.org/x/crypto="v0.0.0-20211108221036-ceb1ce70b4fa"
golang.org/x/image="v0.0.0-20211028202545-6944b10bf410"
golang.org/x/net="v0.0.0-20220425223048-2871e0cb64e4"
golang.org/x/oauth2="v0.0.0-20220411215720-9780585627b5"
golang.org/x/sync="v0.0.0-20210220032951-036812b2e83c"
golang.org/x/sys="v0.0.0-20210908233432-aa78b53d3365"
golang.org/x/sys="v0.0.0-20220422013727-9388b58f7150"
golang.org/x/text="v0.3.7"
golang.org/x/xerrors="v0.0.0-20200804184101-5ec99f83aff1"
google.golang.org/api="v0.51.0"
google.golang.org/genproto="v0.0.0-20210716133855-ce7ef5c701ea"
google.golang.org/grpc="v1.39.0"
google.golang.org/protobuf="v1.27.1"
golang.org/x/tools="v0.1.10"
golang.org/x/xerrors="v0.0.0-20220411194840-2f41105eb62f"
google.golang.org/api="v0.76.0"
google.golang.org/genproto="v0.0.0-20220426171045-31bebdecfb46"
google.golang.org/grpc="v1.46.0"
google.golang.org/protobuf="v1.28.0"
gopkg.in/yaml.v2="v2.4.0"
```
+1 -1
View File
@@ -20,7 +20,7 @@ import (
)
var bufferPool = &sync.Pool{
New: func() interface{} {
New: func() any {
return &bytes.Buffer{}
},
}
+3
View File
@@ -295,6 +295,9 @@ func (c *Cache) isExpired(modTime time.Time) bool {
if c.maxAge < 0 {
return false
}
// Note the use of time.Since here.
// We cannot use Hugo's global Clock for this.
return c.maxAge == 0 || time.Since(modTime) > c.maxAge
}
+8 -6
View File
@@ -14,6 +14,7 @@
package filecache
import (
"fmt"
"path"
"path/filepath"
"strings"
@@ -25,8 +26,9 @@ import (
"github.com/gohugoio/hugo/helpers"
"errors"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/spf13/afero"
)
@@ -153,7 +155,7 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
}
if err := decoder.Decode(v); err != nil {
return nil, errors.Wrap(err, "failed to decode filecache config")
return nil, fmt.Errorf("failed to decode filecache config: %w", err)
}
if cc.Dir == "" {
@@ -162,7 +164,7 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
name := strings.ToLower(k)
if !valid[name] {
return nil, errors.Errorf("%q is not a valid cache name", name)
return nil, fmt.Errorf("%q is not a valid cache name", name)
}
c[name] = cc
@@ -197,12 +199,12 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
if !v.isResourceDir {
if isOsFs && !filepath.IsAbs(v.Dir) {
return c, errors.Errorf("%q must resolve to an absolute directory", v.Dir)
return c, fmt.Errorf("%q must resolve to an absolute directory", v.Dir)
}
// Avoid cache in root, e.g. / (Unix) or c:\ (Windows)
if len(strings.TrimPrefix(v.Dir, filepath.VolumeName(v.Dir))) == 1 {
return c, errors.Errorf("%q is a root folder and not allowed as cache dir", v.Dir)
return c, fmt.Errorf("%q is a root folder and not allowed as cache dir", v.Dir)
}
}
@@ -242,5 +244,5 @@ func resolveDirPlaceholder(fs afero.Fs, cfg config.Provider, placeholder string)
return filepath.Base(workingDir), false, nil
}
return "", false, errors.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder)
return "", false, fmt.Errorf("%q is not a valid placeholder (valid values are :cacheDir or :resourceDir)", placeholder)
}
+1 -1
View File
@@ -184,7 +184,7 @@ dir = "/"
}
func newTestConfig() config.Provider {
cfg := config.New()
cfg := config.NewWithTestDefaults()
cfg.Set("workingDir", filepath.FromSlash("/my/cool/hugoproject"))
cfg.Set("contentDir", "content")
cfg.Set("dataDir", "data")
+2 -2
View File
@@ -14,12 +14,12 @@
package filecache
import (
"fmt"
"io"
"os"
"github.com/gohugoio/hugo/hugofs"
"github.com/pkg/errors"
"github.com/spf13/afero"
)
@@ -39,7 +39,7 @@ func (c Caches) Prune() (int, error) {
if os.IsNotExist(err) {
continue
}
return counter, errors.Wrapf(err, "failed to prune cache %q", k)
return counter, fmt.Errorf("failed to prune cache %q: %w", k, err)
}
}
+1
View File
@@ -342,6 +342,7 @@ func newPathsSpec(t *testing.T, fs afero.Fs, configStr string) *helpers.PathSpec
cfg, err := config.FromConfigString(configStr, "toml")
c.Assert(err, qt.IsNil)
initConfig(fs, cfg)
config.SetBaseTestDefaults(cfg)
p, err := helpers.NewPathSpec(hugofs.NewFrom(fs, cfg), cfg, nil)
c.Assert(err, qt.IsNil)
return p
+2 -2
View File
@@ -30,7 +30,7 @@ type Cache struct {
}
type cacheEntry struct {
value interface{}
value any
err error
}
@@ -55,7 +55,7 @@ func (c *Cache) Clear() {
// create will be called and cached.
// This method is thread safe. It also guarantees that the create func for a given
// key is invoked only once for this cache.
func (c *Cache) GetOrCreate(key string, create func() (interface{}, error)) (interface{}, error) {
func (c *Cache) GetOrCreate(key string, create func() (any, error)) (any, error) {
c.mu.RLock()
entry, found := c.cache[key]
c.mu.RUnlock()
+3 -3
View File
@@ -28,7 +28,7 @@ func TestNamedCache(t *testing.T) {
cache := New()
counter := 0
create := func() (interface{}, error) {
create := func() (any, error) {
counter++
return counter, nil
}
@@ -58,8 +58,8 @@ func TestNamedCacheConcurrent(t *testing.T) {
cache := New()
create := func(i int) func() (interface{}, error) {
return func() (interface{}, error) {
create := func(i int) func() (any, error) {
return func() (any, error) {
return i, nil
}
}
+1 -1
View File
@@ -505,7 +505,7 @@ func typeName(name, pkg string) string {
func uniqueNonEmptyStrings(s []string) []string {
var unique []string
set := map[string]interface{}{}
set := map[string]any{}
for _, val := range s {
if val == "" {
continue
+1 -1
View File
@@ -85,7 +85,7 @@ type I interface {
Method3(myint int, mystring string)
Method5() (string, error)
Method6() *net.IP
Method7() interface{}
Method7() any
Method8() herrors.ErrorContext
method2()
method9() os.FileInfo
+116 -38
View File
@@ -14,9 +14,10 @@
package commands
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"regexp"
@@ -28,8 +29,11 @@ import (
"golang.org/x/sync/semaphore"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/paths"
"github.com/spf13/cast"
jww "github.com/spf13/jwalterweatherman"
"github.com/gohugoio/hugo/common/loggers"
@@ -40,7 +44,9 @@ import (
"github.com/gohugoio/hugo/hugolib"
"github.com/spf13/afero"
"github.com/bep/clock"
"github.com/bep/debounce"
"github.com/bep/overlayfs"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
@@ -72,8 +78,10 @@ type commandeer struct {
// be fast enough that we could maybe just add it for all server modes.
changeDetector *fileChangeDetector
// We need to reuse this on server rebuilds.
destinationFs afero.Fs
// We need to reuse these on server rebuilds.
// These 2 will be different if --renderStaticToDisk is set.
publishDirFs afero.Fs
publishDirServerFs afero.Fs
h *hugoBuilderCommon
ftch flagsToConfigHandler
@@ -88,13 +96,14 @@ type commandeer struct {
// Used in cases where we get flooded with events in server mode.
debounce func(f func())
serverPorts []int
languagesConfigured bool
languages langs.Languages
doLiveReload bool
fastRenderMode bool
showErrorInBrowser bool
wasError bool
serverPorts []serverPortListener
languages langs.Languages
doLiveReload bool
renderStaticToDisk bool
fastRenderMode bool
showErrorInBrowser bool
wasError bool
configured bool
paused bool
@@ -105,6 +114,11 @@ type commandeer struct {
buildErr error
}
type serverPortListener struct {
p int
ln net.Listener
}
func newCommandeerHugoState() *commandeerHugoState {
return &commandeerHugoState{
created: make(chan struct{}),
@@ -116,37 +130,38 @@ func (c *commandeerHugoState) hugo() *hugolib.HugoSites {
return c.hugoSites
}
func (c *commandeerHugoState) hugoTry() *hugolib.HugoSites {
select {
case <-c.created:
return c.hugoSites
case <-time.After(time.Millisecond * 100):
return nil
}
}
func (c *commandeer) errCount() int {
return int(c.logger.LogCounters().ErrorCounter.Count())
}
func (c *commandeer) getErrorWithContext() interface{} {
func (c *commandeer) getErrorWithContext() any {
errCount := c.errCount()
if errCount == 0 {
return nil
}
m := make(map[string]interface{})
m := make(map[string]any)
m["Error"] = errors.New(removeErrorPrefixFromLog(c.logger.Errors()))
//xwm["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.logger.Errors())))
m["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.logger.Errors())))
m["Version"] = hugo.BuildVersionString()
fe := herrors.UnwrapErrorWithFileContext(c.buildErr)
if fe != nil {
m["File"] = fe
}
if c.h.verbose {
var b bytes.Buffer
herrors.FprintStackTraceFromErr(&b, c.buildErr)
m["StackTrace"] = b.String()
}
ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.buildErr)
m["Files"] = ferrors
return m
}
func (c *commandeer) Set(key string, value interface{}) {
func (c *commandeer) Set(key string, value any) {
if c.configured {
panic("commandeer cannot be changed")
}
@@ -154,12 +169,28 @@ func (c *commandeer) Set(key string, value interface{}) {
}
func (c *commandeer) initFs(fs *hugofs.Fs) error {
c.destinationFs = fs.Destination
c.publishDirFs = fs.PublishDir
c.publishDirServerFs = fs.PublishDirServer
c.DepsCfg.Fs = fs
return nil
}
func (c *commandeer) initClock(loc *time.Location) error {
bt := c.Cfg.GetString("clock")
if bt == "" {
return nil
}
t, err := cast.StringToDateInDefaultLocation(bt, loc)
if err != nil {
return fmt.Errorf(`failed to parse "clock" flag: %s`, err)
}
htime.Clock = clock.Start(t)
return nil
}
func newCommandeer(mustHaveConfigFile, failOnInitErr, running bool, h *hugoBuilderCommon, f flagsToConfigHandler, cfgInit func(c *commandeer) error, subCmdVs ...*cobra.Command) (*commandeer, error) {
var rebuildDebouncer func(f func())
if running {
@@ -337,9 +368,16 @@ func (c *commandeer) loadConfig() error {
c.configFiles = configFiles
if l, ok := c.Cfg.Get("languagesSorted").(langs.Languages); ok {
c.languagesConfigured = true
c.languages = l
var ok bool
loc := time.Local
c.languages, ok = c.Cfg.Get("languagesSorted").(langs.Languages)
if ok {
loc = langs.GetLocation(c.languages[0])
}
err = c.initClock(loc)
if err != nil {
return err
}
// Set some commonly used flags
@@ -368,21 +406,57 @@ func (c *commandeer) loadConfig() error {
}
createMemFs := config.GetBool("renderToMemory")
c.renderStaticToDisk = config.GetBool("renderStaticToDisk")
if createMemFs {
// Rendering to memoryFS, publish to Root regardless of publishDir.
config.Set("publishDir", "/")
config.Set("publishDirStatic", "/")
} else if c.renderStaticToDisk {
// Hybrid, render dynamic content to Root.
config.Set("publishDirStatic", config.Get("publishDir"))
config.Set("publishDir", "/")
}
c.fsCreate.Do(func() {
fs := hugofs.NewFrom(sourceFs, config)
// Assume both source and destination are using same filesystem.
fs := hugofs.NewFromSourceAndDestination(sourceFs, sourceFs, config)
if c.destinationFs != nil {
if c.publishDirFs != nil {
// Need to reuse the destination on server rebuilds.
fs.Destination = c.destinationFs
} else if createMemFs {
// Hugo writes the output to memory instead of the disk.
fs.Destination = new(afero.MemMapFs)
fs.PublishDir = c.publishDirFs
fs.PublishDirServer = c.publishDirServerFs
} else {
if c.renderStaticToDisk {
publishDirStatic := config.GetString("publishDirStatic")
workingDir := config.GetString("workingDir")
absPublishDirStatic := paths.AbsPathify(workingDir, publishDirStatic)
fs = hugofs.NewFromSourceAndDestination(sourceFs, afero.NewMemMapFs(), config)
// Writes the dynamic output to memory,
// while serve others directly from /public on disk.
dynamicFs := fs.PublishDir
staticFs := afero.NewBasePathFs(afero.NewOsFs(), absPublishDirStatic)
// Serve from both the static and dynamic fs,
// the first will take priority.
// THis is a read-only filesystem,
// we do all the writes to
// fs.Destination and fs.DestinationStatic.
fs.PublishDirServer = overlayfs.New(
overlayfs.Options{
Fss: []afero.Fs{
dynamicFs,
staticFs,
},
},
)
fs.PublishDirStatic = staticFs
} else if createMemFs {
// Hugo writes the output to memory instead of the disk.
fs = hugofs.NewFromSourceAndDestination(sourceFs, afero.NewMemMapFs(), config)
}
}
if c.fastRenderMode {
@@ -396,12 +470,15 @@ func (c *commandeer) loadConfig() error {
}
changeDetector.PrepareNew()
fs.Destination = hugofs.NewHashingFs(fs.Destination, changeDetector)
fs.PublishDir = hugofs.NewHashingFs(fs.PublishDir, changeDetector)
fs.PublishDirStatic = hugofs.NewHashingFs(fs.PublishDirStatic, changeDetector)
c.changeDetector = changeDetector
}
if c.Cfg.GetBool("logPathWarnings") {
fs.Destination = hugofs.NewCreateCountingFs(fs.Destination)
// Note that we only care about the "dynamic creates" here,
// so skip the static fs.
fs.PublishDir = hugofs.NewCreateCountingFs(fs.PublishDir)
}
// To debug hard-to-find path issues.
@@ -420,9 +497,10 @@ func (c *commandeer) loadConfig() error {
if h == nil || c.failOnInitErr {
err = createErr
}
c.hugoSites = h
// TODO(bep) improve.
if c.buildLock == nil {
if c.buildLock == nil && h != nil {
c.buildLock = h.LockBuild
}
close(c.created)
+14 -11
View File
@@ -18,10 +18,9 @@ import (
"os"
"time"
"github.com/gohugoio/hugo/hugolib/paths"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
hpaths "github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
"github.com/spf13/cobra"
@@ -150,7 +149,7 @@ func (b *commandsBuilder) newHugoCmd() *hugoCmd {
Hugo is a Fast and Flexible Static Site Generator
built with love by spf13 and friends in Go.
Complete documentation is available at http://gohugo.io/.`,
Complete documentation is available at https://gohugo.io/.`,
RunE: func(cmd *cobra.Command, args []string) error {
defer cc.timeTrack(time.Now(), "Total")
cfgInit := func(c *commandeer) error {
@@ -211,6 +210,7 @@ type hugoBuilderCommon struct {
buildWatch bool
poll string
clock string
gc bool
@@ -243,14 +243,14 @@ func (cc *hugoBuilderCommon) timeTrack(start time.Time, name string) {
func (cc *hugoBuilderCommon) getConfigDir(baseDir string) string {
if cc.cfgDir != "" {
return paths.AbsPathify(baseDir, cc.cfgDir)
return hpaths.AbsPathify(baseDir, cc.cfgDir)
}
if v, found := os.LookupEnv("HUGO_CONFIGDIR"); found {
return paths.AbsPathify(baseDir, v)
return hpaths.AbsPathify(baseDir, v)
}
return paths.AbsPathify(baseDir, "config")
return hpaths.AbsPathify(baseDir, "config")
}
func (cc *hugoBuilderCommon) getEnvironment(isServer bool) string {
@@ -280,6 +280,7 @@ func (cc *hugoBuilderCommon) handleCommonBuilderFlags(cmd *cobra.Command) {
cmd.PersistentFlags().StringVarP(&cc.environment, "environment", "e", "", "build environment")
cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory")
cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern")
cmd.PersistentFlags().StringVar(&cc.clock, "clock", "", "set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00")
}
func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
@@ -294,8 +295,8 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
cmd.Flags().BoolP("ignoreCache", "", false, "ignores the cache directory")
cmd.Flags().StringP("destination", "d", "", "filesystem path to write files to")
cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)")
cmd.Flags().StringVarP(&cc.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. http://spf13.com/")
cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date and author info to the pages")
cmd.Flags().StringVarP(&cc.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. https://spf13.com/")
cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date, author, and CODEOWNERS info to the pages")
cmd.Flags().BoolVar(&cc.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build")
cmd.Flags().StringVar(&cc.poll, "poll", "", "set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes")
cmd.Flags().BoolVar(&loggers.PanicOnWarning, "panicOnWarning", false, "panic on first WARNING log")
@@ -304,11 +305,13 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
cmd.Flags().BoolP("forceSyncStatic", "", false, "copy all files when static is changed.")
cmd.Flags().BoolP("noTimes", "", false, "don't sync modification time of files")
cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files")
cmd.Flags().BoolP("i18n-warnings", "", false, "print missing translations")
cmd.Flags().BoolP("path-warnings", "", false, "print warnings on duplicate target paths etc.")
cmd.Flags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file")
cmd.Flags().BoolP("printI18nWarnings", "", false, "print missing translations")
cmd.Flags().BoolP("printPathWarnings", "", false, "print warnings on duplicate target paths etc.")
cmd.Flags().BoolP("printUnusedTemplates", "", false, "print warnings on unused templates.")
cmd.Flags().StringVarP(&cc.cpuprofile, "profile-cpu", "", "", "write cpu profile to `file`")
cmd.Flags().StringVarP(&cc.memprofile, "profile-mem", "", "", "write memory profile to `file`")
cmd.Flags().BoolVarP(&cc.printm, "print-mem", "", false, "print memory usage to screen at intervals")
cmd.Flags().BoolVarP(&cc.printm, "printMemoryUsage", "", false, "print memory usage to screen at intervals")
cmd.Flags().StringVarP(&cc.mutexprofile, "profile-mutex", "", "", "write Mutex profile to `file`")
cmd.Flags().StringVarP(&cc.traceprofile, "trace", "", "", "write trace to `file` (not useful in general)")
+83 -76
View File
@@ -22,8 +22,6 @@ import (
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/htesting"
"github.com/spf13/afero"
"github.com/gohugoio/hugo/hugofs"
@@ -38,26 +36,23 @@ import (
func TestExecute(t *testing.T) {
c := qt.New(t)
createSite := func(c *qt.C) (string, func()) {
dir, clean, err := createSimpleTestSite(t, testSiteConfig{})
c.Assert(err, qt.IsNil)
return dir, clean
createSite := func(c *qt.C) string {
dir := createSimpleTestSite(t, testSiteConfig{})
return dir
}
c.Run("hugo", func(c *qt.C) {
dir, clean := createSite(c)
defer clean()
dir := createSite(c)
resp := Execute([]string{"-s=" + dir})
c.Assert(resp.Err, qt.IsNil)
result := resp.Result
c.Assert(len(result.Sites) == 1, qt.Equals, true)
c.Assert(len(result.Sites[0].RegularPages()) == 1, qt.Equals, true)
c.Assert(len(result.Sites[0].RegularPages()) == 2, qt.Equals, true)
c.Assert(result.Sites[0].Info.Params()["myparam"], qt.Equals, "paramproduction")
})
c.Run("hugo, set environment", func(c *qt.C) {
dir, clean := createSite(c)
defer clean()
dir := createSite(c)
resp := Execute([]string{"-s=" + dir, "-e=staging"})
c.Assert(resp.Err, qt.IsNil)
result := resp.Result
@@ -65,9 +60,8 @@ func TestExecute(t *testing.T) {
})
c.Run("convert toJSON", func(c *qt.C) {
dir, clean := createSite(c)
dir := createSite(c)
output := filepath.Join(dir, "myjson")
defer clean()
resp := Execute([]string{"convert", "toJSON", "-s=" + dir, "-e=staging", "-o=" + output})
c.Assert(resp.Err, qt.IsNil)
converted := readFileFrom(c, filepath.Join(output, "content", "p1.md"))
@@ -75,8 +69,7 @@ func TestExecute(t *testing.T) {
})
c.Run("config, set environment", func(c *qt.C) {
dir, clean := createSite(c)
defer clean()
dir := createSite(c)
out, err := captureStdout(func() error {
resp := Execute([]string{"config", "-s=" + dir, "-e=staging"})
return resp.Err
@@ -86,16 +79,14 @@ func TestExecute(t *testing.T) {
})
c.Run("deploy, environment set", func(c *qt.C) {
dir, clean := createSite(c)
defer clean()
dir := createSite(c)
resp := Execute([]string{"deploy", "-s=" + dir, "-e=staging", "--target=mydeployment", "--dryRun"})
c.Assert(resp.Err, qt.Not(qt.IsNil))
c.Assert(resp.Err.Error(), qt.Contains, `no driver registered for "hugocloud"`)
})
c.Run("list", func(c *qt.C) {
dir, clean := createSite(c)
defer clean()
dir := createSite(c)
out, err := captureStdout(func() error {
resp := Execute([]string{"list", "all", "-s=" + dir, "-e=staging"})
return resp.Err
@@ -105,8 +96,7 @@ func TestExecute(t *testing.T) {
})
c.Run("new theme", func(c *qt.C) {
dir, clean := createSite(c)
defer clean()
dir := createSite(c)
themesDir := filepath.Join(dir, "mythemes")
resp := Execute([]string{"new", "theme", "mytheme", "-s=" + dir, "-e=staging", "--themesDir=" + themesDir})
c.Assert(resp.Err, qt.IsNil)
@@ -115,8 +105,7 @@ func TestExecute(t *testing.T) {
})
c.Run("new site", func(c *qt.C) {
dir, clean := createSite(c)
defer clean()
dir := createSite(c)
siteDir := filepath.Join(dir, "mysite")
resp := Execute([]string{"new", "site", siteDir, "-e=staging"})
c.Assert(resp.Err, qt.IsNil)
@@ -167,7 +156,7 @@ func TestFlags(t *testing.T) {
name: "ignoreVendorPaths",
args: []string{"server", "--ignoreVendorPaths=github.com/**"},
check: func(c *qt.C, cmd *serverCmd) {
cfg := config.New()
cfg := config.NewWithTestDefaults()
cmd.flagsToConfig(cfg)
c.Assert(cfg.Get("ignoreVendorPaths"), qt.Equals, "github.com/**")
},
@@ -190,13 +179,14 @@ func TestFlags(t *testing.T) {
"--navigateToChanged",
"--disableLiveReload",
"--noHTTPCache",
"--i18n-warnings",
"--printI18nWarnings",
"--destination=/tmp/mydestination",
"-b=https://example.com/b/",
"--port=1366",
"--renderToDisk",
"--source=mysource",
"--path-warnings",
"--printPathWarnings",
"--printUnusedTemplates",
},
check: func(c *qt.C, sc *serverCmd) {
c.Assert(sc, qt.Not(qt.IsNil))
@@ -207,7 +197,7 @@ func TestFlags(t *testing.T) {
c.Assert(sc.serverPort, qt.Equals, 1366)
c.Assert(sc.environment, qt.Equals, "testing")
cfg := config.New()
cfg := config.NewWithTestDefaults()
sc.flagsToConfig(cfg)
c.Assert(cfg.GetString("publishDir"), qt.Equals, "/tmp/mydestination")
c.Assert(cfg.GetString("contentDir"), qt.Equals, "mycontent")
@@ -220,10 +210,10 @@ func TestFlags(t *testing.T) {
c.Assert(cfg.GetBool("gc"), qt.Equals, true)
// The flag is named path-warnings
// The flag is named printPathWarnings
c.Assert(cfg.GetBool("logPathWarnings"), qt.Equals, true)
// The flag is named i18n-warnings
// The flag is named printI18nWarnings
c.Assert(cfg.GetBool("logI18nWarnings"), qt.Equals, true)
},
},
@@ -252,14 +242,8 @@ func TestFlags(t *testing.T) {
func TestCommandsExecute(t *testing.T) {
c := qt.New(t)
dir, clean, err := createSimpleTestSite(t, testSiteConfig{})
c.Assert(err, qt.IsNil)
dirOut, clean2, err := htesting.CreateTempDir(hugofs.Os, "hugo-cli-out")
c.Assert(err, qt.IsNil)
defer clean()
defer clean2()
dir := createSimpleTestSite(t, testSiteConfig{})
dirOut := t.TempDir()
sourceFlag := fmt.Sprintf("-s=%s", dir)
@@ -296,29 +280,35 @@ func TestCommandsExecute(t *testing.T) {
}
for _, test := range tests {
b := newCommandsBuilder().addAll().build()
hugoCmd := b.getCommand()
test.flags = append(test.flags, "--quiet")
hugoCmd.SetArgs(append(test.commands, test.flags...))
// TODO(bep) capture output and add some simple asserts
// TODO(bep) misspelled subcommands does not return an error. We should investigate this
// but before that, check for "Error: unknown command".
_, err := hugoCmd.ExecuteC()
if test.expectErrToContain != "" {
c.Assert(err, qt.Not(qt.IsNil))
c.Assert(err.Error(), qt.Contains, test.expectErrToContain)
} else {
c.Assert(err, qt.IsNil)
name := "hugo"
if len(test.commands) > 0 {
name = test.commands[0]
}
c.Run(name, func(c *qt.C) {
b := newCommandsBuilder().addAll().build()
hugoCmd := b.getCommand()
test.flags = append(test.flags, "--quiet")
hugoCmd.SetArgs(append(test.commands, test.flags...))
// Assert that we have not left any development debug artifacts in
// the code.
if b.c != nil {
_, ok := b.c.destinationFs.(types.DevMarker)
c.Assert(ok, qt.Equals, false)
}
// TODO(bep) capture output and add some simple asserts
// TODO(bep) misspelled subcommands does not return an error. We should investigate this
// but before that, check for "Error: unknown command".
_, err := hugoCmd.ExecuteC()
if test.expectErrToContain != "" {
c.Assert(err, qt.Not(qt.IsNil))
c.Assert(err.Error(), qt.Contains, test.expectErrToContain)
} else {
c.Assert(err, qt.IsNil)
}
// Assert that we have not left any development debug artifacts in
// the code.
if b.c != nil {
_, ok := b.c.publishDirFs.(types.DevMarker)
c.Assert(ok, qt.Equals, false)
}
})
}
}
@@ -328,11 +318,8 @@ type testSiteConfig struct {
contentDir string
}
func createSimpleTestSite(t *testing.T, cfg testSiteConfig) (string, func(), error) {
d, clean, e := htesting.CreateTempDir(hugofs.Os, "hugo-cli")
if e != nil {
return "", nil, e
}
func createSimpleTestSite(t testing.TB, cfg testSiteConfig) string {
dir := t.TempDir()
cfgStr := `
@@ -351,21 +338,23 @@ title = "Hugo Commands"
contentDir = cfg.contentDir
}
os.MkdirAll(filepath.Join(d, "public"), 0777)
os.MkdirAll(filepath.Join(dir, "public"), 0777)
// Just the basic. These are for CLI tests, not site testing.
writeFile(t, filepath.Join(d, "config.toml"), cfgStr)
writeFile(t, filepath.Join(d, "config", "staging", "params.toml"), `myparam="paramstaging"`)
writeFile(t, filepath.Join(d, "config", "staging", "deployment.toml"), `
writeFile(t, filepath.Join(dir, "config.toml"), cfgStr)
writeFile(t, filepath.Join(dir, "config", "staging", "params.toml"), `myparam="paramstaging"`)
writeFile(t, filepath.Join(dir, "config", "staging", "deployment.toml"), `
[[targets]]
name = "mydeployment"
URL = "hugocloud://hugotestbucket"
`)
writeFile(t, filepath.Join(d, "config", "testing", "params.toml"), `myparam="paramtesting"`)
writeFile(t, filepath.Join(d, "config", "production", "params.toml"), `myparam="paramproduction"`)
writeFile(t, filepath.Join(dir, "config", "testing", "params.toml"), `myparam="paramtesting"`)
writeFile(t, filepath.Join(dir, "config", "production", "params.toml"), `myparam="paramproduction"`)
writeFile(t, filepath.Join(d, contentDir, "p1.md"), `
writeFile(t, filepath.Join(dir, "static", "myfile.txt"), `Hello World!`)
writeFile(t, filepath.Join(dir, contentDir, "p1.md"), `
---
title: "P1"
weight: 1
@@ -375,28 +364,46 @@ Content
`)
writeFile(t, filepath.Join(d, "layouts", "_default", "single.html"), `
writeFile(t, filepath.Join(dir, contentDir, "hügö.md"), `
---
weight: 2
---
Single: {{ .Title }}
This is hügö.
`)
writeFile(t, filepath.Join(d, "layouts", "_default", "list.html"), `
writeFile(t, filepath.Join(dir, "layouts", "_default", "single.html"), `
Single: {{ .Title }}|{{ .Content }}
`)
writeFile(t, filepath.Join(dir, "layouts", "404.html"), `
404: {{ .Title }}|Not Found.
`)
writeFile(t, filepath.Join(dir, "layouts", "_default", "list.html"), `
List: {{ .Title }}
Environment: {{ hugo.Environment }}
For issue 9788:
{{ $foo :="abc" | resources.FromString "foo.css" | minify | resources.PostProcess }}
PostProcess: {{ $foo.RelPermalink }}
`)
return d, clean, nil
return dir
}
func writeFile(t *testing.T, filename, content string) {
func writeFile(t testing.TB, filename, content string) {
must(t, os.MkdirAll(filepath.Dir(filename), os.FileMode(0755)))
must(t, ioutil.WriteFile(filename, []byte(content), os.FileMode(0755)))
}
func must(t *testing.T, err error) {
func must(t testing.TB, err error) {
if err != nil {
t.Fatal(err)
}
+7 -7
View File
@@ -145,13 +145,13 @@ func (m *modMounts) MarshalJSON() ([]byte, error) {
if m.verbose {
config := m.m.Config()
return json.Marshal(&struct {
Path string `json:"path"`
Version string `json:"version"`
Time time.Time `json:"time"`
Owner string `json:"owner"`
Dir string `json:"dir"`
Meta map[string]interface{} `json:"meta"`
HugoVersion modules.HugoVersion `json:"hugoVersion"`
Path string `json:"path"`
Version string `json:"version"`
Time time.Time `json:"time"`
Owner string `json:"owner"`
Dir string `json:"dir"`
Meta map[string]any `json:"meta"`
HugoVersion modules.HugoVersion `json:"hugoVersion"`
Mounts []modMount `json:"mounts"`
}{
+2 -4
View File
@@ -31,8 +31,6 @@ import (
"github.com/gohugoio/hugo/parser"
"github.com/gohugoio/hugo/parser/metadecoders"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/hugolib"
"github.com/spf13/cobra"
@@ -193,7 +191,7 @@ func (cc *convertCmd) convertAndSavePage(p page.Page, site *hugolib.Site, target
fs := hugofs.Os
if err := helpers.WriteToDisk(newFilename, &newContent, fs); err != nil {
return errors.Wrapf(err, "Failed to save file %q:", newFilename)
return fmt.Errorf("Failed to save file %q:: %w", newFilename, err)
}
return nil
@@ -202,7 +200,7 @@ func (cc *convertCmd) convertAndSavePage(p page.Page, site *hugolib.Site, target
type parsedFile struct {
frontMatterFormat metadecoders.Format
frontMatterSource []byte
frontMatter map[string]interface{}
frontMatter map[string]any
// Everything after Front Matter
content []byte
+1
View File
@@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !nodeploy
// +build !nodeploy
package commands
+3 -3
View File
@@ -16,9 +16,9 @@ package commands
import (
"os"
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/formatters/html"
"github.com/alecthomas/chroma/styles"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/styles"
"github.com/spf13/cobra"
)
+1 -1
View File
@@ -49,7 +49,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 http://gohugo.io/.
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.`,
+3 -3
View File
@@ -53,15 +53,15 @@ func (c commandError) isUserError() bool {
return c.userError
}
func newUserError(a ...interface{}) commandError {
func newUserError(a ...any) commandError {
return commandError{s: fmt.Sprintln(a...), userError: true}
}
func newSystemError(a ...interface{}) commandError {
func newSystemError(a ...any) commandError {
return commandError{s: fmt.Sprintln(a...), userError: false}
}
func newSystemErrorF(format string, a ...interface{}) commandError {
func newSystemErrorF(format string, a ...any) commandError {
return commandError{s: fmt.Sprintf(format, a...), userError: false}
}
+56 -35
View File
@@ -31,16 +31,16 @@ import (
"time"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/tpl"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/resources/page"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/terminal"
@@ -127,6 +127,12 @@ func initializeConfig(mustHaveConfigFile, failOnInitErr, running bool,
return nil, err
}
if h := c.hugoTry(); h != nil {
for _, s := range h.Sites {
s.RegisterMediaTypes()
}
}
return c, nil
}
@@ -189,6 +195,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
"buildDrafts",
"buildFuture",
"buildExpired",
"clock",
"uglyURLs",
"canonifyURLs",
"enableRobotsTXT",
@@ -199,6 +206,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
"forceSyncStatic",
"noTimes",
"noChmod",
"noBuildLock",
"ignoreVendorPaths",
"templateMetrics",
"templateMetricsHints",
@@ -216,7 +224,8 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
"dryRun",
"force",
"gc",
"i18n-warnings",
"printI18nWarnings",
"printUnusedTemplates",
"invalidateCDN",
"layoutDir",
"logFile",
@@ -243,8 +252,8 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
// Set some "config aliases"
setValueFromFlag(cmd.Flags(), "destination", cfg, "publishDir", false)
setValueFromFlag(cmd.Flags(), "i18n-warnings", cfg, "logI18nWarnings", false)
setValueFromFlag(cmd.Flags(), "path-warnings", cfg, "logPathWarnings", false)
setValueFromFlag(cmd.Flags(), "printI18nWarnings", cfg, "logI18nWarnings", false)
setValueFromFlag(cmd.Flags(), "printPathWarnings", cfg, "logPathWarnings", false)
}
func setValueFromFlag(flags *flag.FlagSet, key string, cfg config.Provider, targetKey string, force bool) {
@@ -275,10 +284,6 @@ func setValueFromFlag(flags *flag.FlagSet, key string, cfg config.Provider, targ
}
}
func isTerminal() bool {
return terminal.IsTerminal(os.Stdout)
}
func (c *commandeer) fullBuild(noBuildLock bool) error {
var (
g errgroup.Group
@@ -288,7 +293,7 @@ func (c *commandeer) fullBuild(noBuildLock bool) error {
if !c.h.quiet {
fmt.Println("Start building sites … ")
fmt.Println(hugo.BuildVersionString())
if isTerminal() {
if terminal.IsTerminal(os.Stdout) {
defer func() {
fmt.Print(showCursor + clearLine)
}()
@@ -298,14 +303,14 @@ func (c *commandeer) fullBuild(noBuildLock bool) error {
copyStaticFunc := func() error {
cnt, err := c.copyStatic()
if err != nil {
return errors.Wrap(err, "Error copying static files")
return fmt.Errorf("Error copying static files: %w", err)
}
langCount = cnt
return nil
}
buildSitesFunc := func() error {
if err := c.buildSites(noBuildLock); err != nil {
return errors.Wrap(err, "Error building site")
return fmt.Errorf("Error building site: %w", err)
}
return nil
}
@@ -352,10 +357,10 @@ func (c *commandeer) initCPUProfile() (func(), error) {
f, err := os.Create(c.h.cpuprofile)
if err != nil {
return nil, errors.Wrap(err, "failed to create CPU profile")
return nil, fmt.Errorf("failed to create CPU profile: %w", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
return nil, errors.Wrap(err, "failed to start CPU profile")
return nil, fmt.Errorf("failed to start CPU profile: %w", err)
}
return func() {
pprof.StopCPUProfile()
@@ -386,11 +391,11 @@ func (c *commandeer) initTraceProfile() (func(), error) {
f, err := os.Create(c.h.traceprofile)
if err != nil {
return nil, errors.Wrap(err, "failed to create trace file")
return nil, fmt.Errorf("failed to create trace file: %w", err)
}
if err := trace.Start(f); err != nil {
return nil, errors.Wrap(err, "failed to start trace")
return nil, fmt.Errorf("failed to start trace: %w", err)
}
return func() {
@@ -501,17 +506,24 @@ func (c *commandeer) build() error {
return err
}
// TODO(bep) Feedback?
if !c.h.quiet {
fmt.Println()
c.hugo().PrintProcessingStats(os.Stdout)
fmt.Println()
if createCounter, ok := c.destinationFs.(hugofs.DuplicatesReporter); ok {
dupes := createCounter.ReportDuplicates()
if dupes != "" {
c.logger.Warnln("Duplicate target paths:", dupes)
hugofs.WalkFilesystems(c.publishDirFs, func(fs afero.Fs) bool {
if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
dupes := dfs.ReportDuplicates()
if dupes != "" {
c.logger.Warnln("Duplicate target paths:", dupes)
}
}
return false
})
unusedTemplates := c.hugo().Tmpl().(tpl.UnusedTemplatesProvider).UnusedTemplates()
for _, unusedTemplate := range unusedTemplates {
c.logger.Warnf("Template %s is unused, source file %s", unusedTemplate.Name(), unusedTemplate.Filename())
}
}
@@ -628,11 +640,7 @@ func chmodFilter(dst, src os.FileInfo) bool {
}
func (c *commandeer) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
publishDir := c.hugo().PathSpec.PublishDir
// If root, remove the second '/'
if publishDir == "//" {
publishDir = helpers.FilePathSeparator
}
publishDir := helpers.FilePathSeparator
if sourceFs.PublishFolder != "" {
publishDir = filepath.Join(publishDir, sourceFs.PublishFolder)
@@ -645,7 +653,10 @@ func (c *commandeer) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint6
syncer.NoChmod = c.Cfg.GetBool("noChmod")
syncer.ChmodFilter = chmodFilter
syncer.SrcFs = fs
syncer.DestFs = c.Fs.Destination
syncer.DestFs = c.Fs.PublishDir
if c.renderStaticToDisk {
syncer.DestFs = c.Fs.PublishDirStatic
}
// Now that we are using a unionFs for the static directories
// We can effectively clean the publishDir on initial sync
syncer.Delete = c.Cfg.GetBool("cleanDestinationDir")
@@ -677,6 +688,10 @@ func (c *commandeer) firstPathSpec() *helpers.PathSpec {
}
func (c *commandeer) timeTrack(start time.Time, name string) {
// Note the use of time.Since here and time.Now in the callers.
// We have a htime.Sinnce, but that may be adjusted to the future,
// and that does not make sense here, esp. when used before the
// global Clock is initialized.
elapsed := time.Since(start)
c.logger.Printf("%s in %v ms", name, int(1000*elapsed.Seconds()))
}
@@ -727,15 +742,16 @@ func (c *commandeer) buildSites(noBuildLock bool) (err error) {
func (c *commandeer) handleBuildErr(err error, msg string) {
c.buildErr = err
c.logger.Errorln(msg + ":\n")
c.logger.Errorln(helpers.FirstUpper(err.Error()))
if !c.h.quiet && c.h.verbose {
herrors.PrintStackTraceFromErr(err)
}
c.logger.Errorln(msg + ": " + cleanErrorLog(err.Error()))
}
func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
if c.buildErr != nil {
ferrs := herrors.UnwrapFileErrorsWithErrorContext(c.buildErr)
for _, err := range ferrs {
events = append(events, fsnotify.Event{Name: err.Position().Filename, Op: fsnotify.Write})
}
}
c.buildErr = nil
visited := c.visitedURLs.PeekAllSet()
if c.fastRenderMode {
@@ -850,8 +866,13 @@ func (c *commandeer) newWatcher(pollIntervalStr string, dirList ...string) (*wat
return nil, err
}
spec := c.hugo().Deps.SourceSpec
for _, d := range dirList {
if d != "" {
if spec.IgnoreFile(d) {
continue
}
_ = watcher.Add(d)
}
}
@@ -900,7 +921,7 @@ func (c *commandeer) printChangeDetected(typ string) {
c.logger.Println(msg)
const layout = "2006-01-02 15:04:05.000 -0700"
c.logger.Println(time.Now().Format(layout))
c.logger.Println(htime.Now().Format(layout))
}
const (
+176 -17
View File
@@ -14,34 +14,193 @@
package commands
import (
"bytes"
"fmt"
"math/rand"
"path/filepath"
"strings"
"testing"
"github.com/bep/clock"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/hugofs"
"github.com/spf13/afero"
"golang.org/x/tools/txtar"
)
// Issue #5662
func TestHugoWithContentDirOverride(t *testing.T) {
t.Parallel()
c := qt.New(t)
files := `
-- config.toml --
baseURL = "https://example.org"
title = "Hugo Commands"
-- mycontent/p1.md --
---
title: "P1"
---
-- layouts/_default/single.html --
Page: {{ .Title }}|
`
s := newTestHugoCmdBuilder(c, files, []string{"-c", "mycontent"}).Build()
s.AssertFileContent("public/p1/index.html", `Page: P1|`)
}
// Issue #9794
func TestHugoStaticFilesMultipleStaticAndManyFolders(t *testing.T) {
t.Parallel()
c := qt.New(t)
files := `
-- config.toml --
baseURL = "https://example.org"
theme = "mytheme"
-- layouts/index.html --
Home.
`
const (
numDirs = 33
numFilesMax = 12
)
r := rand.New(rand.NewSource(32))
for i := 0; i < numDirs; i++ {
for j := 0; j < r.Intn(numFilesMax); j++ {
if j%3 == 0 {
files += fmt.Sprintf("-- themes/mytheme/static/d%d/f%d.txt --\nHellot%d-%d\n", i, j, i, j)
files += fmt.Sprintf("-- themes/mytheme/static/d%d/ft%d.txt --\nHellot%d-%d\n", i, j, i, j)
}
files += fmt.Sprintf("-- static/d%d/f%d.txt --\nHello%d-%d\n", i, j, i, j)
}
}
r = rand.New(rand.NewSource(32))
s := newTestHugoCmdBuilder(c, files, []string{"-c", "mycontent"}).Build()
for i := 0; i < numDirs; i++ {
for j := 0; j < r.Intn(numFilesMax); j++ {
if j%3 == 0 {
if j%3 == 0 {
s.AssertFileContent(fmt.Sprintf("public/d%d/ft%d.txt", i, j), fmt.Sprintf("Hellot%d-%d", i, j))
}
s.AssertFileContent(fmt.Sprintf("public/d%d/f%d.txt", i, j), fmt.Sprintf("Hello%d-%d", i, j))
}
}
}
}
// Issue #8787
func TestHugoListCommandsWithClockFlag(t *testing.T) {
t.Cleanup(func() { htime.Clock = clock.System() })
c := qt.New(t)
files := `
-- config.toml --
baseURL = "https://example.org"
title = "Hugo Commands"
timeZone = "UTC"
-- content/past.md --
---
title: "Past"
date: 2000-11-06
---
-- content/future.md --
---
title: "Future"
date: 2200-11-06
---
-- layouts/_default/single.html --
Page: {{ .Title }}|
`
s := newTestHugoCmdBuilder(c, files, []string{"list", "future"})
s.captureOut = true
s.Build()
p := filepath.Join("content", "future.md")
s.AssertStdout(p + ",2200-11-06T00:00:00Z")
s = newTestHugoCmdBuilder(c, files, []string{"list", "future", "--clock", "2300-11-06"}).Build()
s.AssertStdout("")
}
type testHugoCmdBuilder struct {
*qt.C
fs afero.Fs
dir string
files string
args []string
captureOut bool
out string
}
func newTestHugoCmdBuilder(c *qt.C, files string, args []string) *testHugoCmdBuilder {
s := &testHugoCmdBuilder{C: c, files: files, args: args}
s.dir = s.TempDir()
s.fs = afero.NewBasePathFs(hugofs.Os, s.dir)
return s
}
func (s *testHugoCmdBuilder) Build() *testHugoCmdBuilder {
data := txtar.Parse([]byte(s.files))
for _, f := range data.Files {
filename := filepath.Clean(f.Name)
data := bytes.TrimSuffix(f.Data, []byte("\n"))
s.Assert(s.fs.MkdirAll(filepath.Dir(filename), 0777), qt.IsNil)
s.Assert(afero.WriteFile(s.fs, filename, data, 0666), qt.IsNil)
}
hugoCmd := newCommandsBuilder().addAll().build()
cmd := hugoCmd.getCommand()
args := append(s.args, "-s="+s.dir, "--quiet")
cmd.SetArgs(args)
contentDir := "contentOverride"
if s.captureOut {
out, err := captureStdout(func() error {
_, err := cmd.ExecuteC()
return err
})
s.Assert(err, qt.IsNil)
s.out = out
} else {
_, err := cmd.ExecuteC()
s.Assert(err, qt.IsNil)
}
cfgStr := `
baseURL = "https://example.org"
title = "Hugo Commands"
contentDir = "thisdoesnotexist"
`
dir, clean, err := createSimpleTestSite(t, testSiteConfig{configTOML: cfgStr, contentDir: contentDir})
c.Assert(err, qt.IsNil)
defer clean()
cmd.SetArgs([]string{"-s=" + dir, "-c=" + contentDir})
_, err = cmd.ExecuteC()
c.Assert(err, qt.IsNil)
return s
}
func (s *testHugoCmdBuilder) AssertFileContent(filename string, matches ...string) {
s.Helper()
data, err := afero.ReadFile(s.fs, filename)
s.Assert(err, qt.IsNil)
content := strings.TrimSpace(string(data))
for _, m := range matches {
lines := strings.Split(m, "\n")
for _, match := range lines {
match = strings.TrimSpace(match)
if match == "" || strings.HasPrefix(match, "#") {
continue
}
s.Assert(content, qt.Contains, match, qt.Commentf(m))
}
}
}
func (s *testHugoCmdBuilder) AssertStdout(match string) {
s.Helper()
content := strings.TrimSpace(s.out)
s.Assert(content, qt.Contains, strings.TrimSpace(match))
}
+9 -14
View File
@@ -28,6 +28,7 @@ import (
"github.com/gohugoio/hugo/parser/pageparser"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/parser/metadecoders"
@@ -35,7 +36,6 @@ import (
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/parser"
"github.com/spf13/afero"
"github.com/spf13/cobra"
@@ -201,12 +201,7 @@ func (i *importCmd) retrieveJekyllPostDir(fs afero.Fs, dir string) (bool, bool)
}
func (i *importCmd) createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[string]bool, force bool) error {
s, err := hugolib.NewSiteDefaultLang()
if err != nil {
return err
}
fs := s.Fs.Source
fs := &afero.OsFs{}
if exists, _ := helpers.Exists(targetDir, fs); exists {
if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
return errors.New("target path \"" + targetDir + "\" exists but is not a directory")
@@ -235,7 +230,7 @@ func (i *importCmd) createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPos
return nil
}
func (i *importCmd) loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]interface{} {
func (i *importCmd) loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]any {
path := filepath.Join(jekyllRoot, "_config.yml")
exists, err := helpers.Exists(path, fs)
@@ -265,7 +260,7 @@ func (i *importCmd) loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]
return c
}
func (i *importCmd) createConfigFromJekyll(fs afero.Fs, inpath string, kind metadecoders.Format, jekyllConfig map[string]interface{}) (err error) {
func (i *importCmd) createConfigFromJekyll(fs afero.Fs, inpath string, kind metadecoders.Format, jekyllConfig map[string]any) (err error) {
title := "My New Hugo Site"
baseURL := "http://example.org/"
@@ -285,7 +280,7 @@ func (i *importCmd) createConfigFromJekyll(fs afero.Fs, inpath string, kind meta
}
}
in := map[string]interface{}{
in := map[string]any{
"baseURL": baseURL,
"title": title,
"languageCode": "en-us",
@@ -362,12 +357,12 @@ func parseJekyllFilename(filename string) (time.Time, string, error) {
re := regexp.MustCompile(`(\d+-\d+-\d+)-(.+)\..*`)
r := re.FindAllStringSubmatch(filename, -1)
if len(r) == 0 {
return time.Now(), "", errors.New("filename not match")
return htime.Now(), "", errors.New("filename not match")
}
postDate, err := time.Parse("2006-1-2", r[0][1])
if err != nil {
return time.Now(), "", err
return htime.Now(), "", err
}
postName := r[0][2]
@@ -423,7 +418,7 @@ func convertJekyllPost(path, relPath, targetDir string, draft bool) error {
return nil
}
func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, draft bool) (interface{}, error) {
func convertJekyllMetaData(m any, postName string, postDate time.Time, draft bool) (any, error) {
metadata, err := maps.ToStringMapE(m)
if err != nil {
return nil, err
@@ -475,7 +470,7 @@ func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, d
return metadata, nil
}
func convertJekyllContent(m interface{}, content string) (string, error) {
func convertJekyllContent(m any, content string) (string, error) {
metadata, _ := maps.ToStringMapE(m)
lines := strings.Split(content, "\n")
+23 -23
View File
@@ -47,44 +47,44 @@ func TestParseJekyllFilename(t *testing.T) {
func TestConvertJekyllMetadata(t *testing.T) {
c := qt.New(t)
testDataList := []struct {
metadata interface{}
metadata any
postName string
postDate time.Time
draft bool
expect string
}{
{
map[interface{}]interface{}{},
map[any]any{},
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
`{"date":"2015-10-01T00:00:00Z"}`,
},
{
map[interface{}]interface{}{},
map[any]any{},
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), true,
`{"date":"2015-10-01T00:00:00Z","draft":true}`,
},
{
map[interface{}]interface{}{"Permalink": "/permalink.html", "layout": "post"},
map[any]any{"Permalink": "/permalink.html", "layout": "post"},
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
`{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`,
},
{
map[interface{}]interface{}{"permalink": "/permalink.html"},
map[any]any{"permalink": "/permalink.html"},
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
`{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`,
},
{
map[interface{}]interface{}{"category": nil, "permalink": 123},
map[any]any{"category": nil, "permalink": 123},
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
`{"date":"2015-10-01T00:00:00Z"}`,
},
{
map[interface{}]interface{}{"Excerpt_Separator": "sep"},
map[any]any{"Excerpt_Separator": "sep"},
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
`{"date":"2015-10-01T00:00:00Z","excerpt_separator":"sep"}`,
},
{
map[interface{}]interface{}{"category": "book", "layout": "post", "Others": "Goods", "Date": "2015-10-01 12:13:11"},
map[any]any{"category": "book", "layout": "post", "Others": "Goods", "Date": "2015-10-01 12:13:11"},
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
`{"Others":"Goods","categories":["book"],"date":"2015-10-01T12:13:11Z"}`,
},
@@ -102,69 +102,69 @@ func TestConvertJekyllMetadata(t *testing.T) {
func TestConvertJekyllContent(t *testing.T) {
c := qt.New(t)
testDataList := []struct {
metadata interface{}
metadata any
content string
expect string
}{
{
map[interface{}]interface{}{},
map[any]any{},
"Test content\r\n<!-- more -->\npart2 content", "Test content\n<!--more-->\npart2 content",
},
{
map[interface{}]interface{}{},
map[any]any{},
"Test content\n<!-- More -->\npart2 content", "Test content\n<!--more-->\npart2 content",
},
{
map[interface{}]interface{}{"excerpt_separator": "<!--sep-->"},
map[any]any{"excerpt_separator": "<!--sep-->"},
"Test content\n<!--sep-->\npart2 content",
"---\nexcerpt_separator: <!--sep-->\n---\nTest content\n<!--more-->\npart2 content",
},
{map[interface{}]interface{}{}, "{% raw %}text{% endraw %}", "text"},
{map[interface{}]interface{}{}, "{%raw%} text2 {%endraw %}", "text2"},
{map[any]any{}, "{% raw %}text{% endraw %}", "text"},
{map[any]any{}, "{%raw%} text2 {%endraw %}", "text2"},
{
map[interface{}]interface{}{},
map[any]any{},
"{% highlight go %}\nvar s int\n{% endhighlight %}",
"{{< highlight go >}}\nvar s int\n{{< / highlight >}}",
},
{
map[interface{}]interface{}{},
map[any]any{},
"{% highlight go linenos hl_lines=\"1 2\" %}\nvar s string\nvar i int\n{% endhighlight %}",
"{{< highlight go \"linenos=table,hl_lines=1 2\" >}}\nvar s string\nvar i int\n{{< / highlight >}}",
},
// Octopress image tag
{
map[interface{}]interface{}{},
map[any]any{},
"{% img http://placekitten.com/890/280 %}",
"{{< figure src=\"http://placekitten.com/890/280\" >}}",
},
{
map[interface{}]interface{}{},
map[any]any{},
"{% img left http://placekitten.com/320/250 Place Kitten #2 %}",
"{{< figure class=\"left\" src=\"http://placekitten.com/320/250\" title=\"Place Kitten #2\" >}}",
},
{
map[interface{}]interface{}{},
map[any]any{},
"{% img right http://placekitten.com/300/500 150 250 'Place Kitten #3' %}",
"{{< figure class=\"right\" src=\"http://placekitten.com/300/500\" width=\"150\" height=\"250\" title=\"Place Kitten #3\" >}}",
},
{
map[interface{}]interface{}{},
map[any]any{},
"{% img right http://placekitten.com/300/500 150 250 'Place Kitten #4' 'An image of a very cute kitten' %}",
"{{< figure class=\"right\" src=\"http://placekitten.com/300/500\" width=\"150\" height=\"250\" title=\"Place Kitten #4\" alt=\"An image of a very cute kitten\" >}}",
},
{
map[interface{}]interface{}{},
map[any]any{},
"{% img http://placekitten.com/300/500 150 250 'Place Kitten #4' 'An image of a very cute kitten' %}",
"{{< figure src=\"http://placekitten.com/300/500\" width=\"150\" height=\"250\" title=\"Place Kitten #4\" alt=\"An image of a very cute kitten\" >}}",
},
{
map[interface{}]interface{}{},
map[any]any{},
"{% img right /placekitten/300/500 'Place Kitten #4' 'An image of a very cute kitten' %}",
"{{< figure class=\"right\" src=\"/placekitten/300/500\" title=\"Place Kitten #4\" alt=\"An image of a very cute kitten\" >}}",
},
{
map[interface{}]interface{}{"category": "book", "layout": "post", "Date": "2015-10-01 12:13:11"},
map[any]any{"category": "book", "layout": "post", "Date": "2015-10-01 12:13:11"},
"somecontent",
"---\nDate: \"2015-10-01 12:13:11\"\ncategory: book\nlayout: post\n---\nsomecontent",
},
+1
View File
@@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !darwin
// +build !darwin
package commands
+13 -5
View File
@@ -32,7 +32,7 @@ type listCmd struct {
*baseBuilderCmd
}
func (lc *listCmd) buildSites(config map[string]interface{}) (*hugolib.HugoSites, error) {
func (lc *listCmd) buildSites(config map[string]any) (*hugolib.HugoSites, error) {
cfgInit := func(c *commandeer) error {
for key, value := range config {
c.Set(key, value)
@@ -75,7 +75,7 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.",
Short: "List all drafts",
Long: `List all of the drafts in your content directory.`,
RunE: func(cmd *cobra.Command, args []string) error {
sites, err := cc.buildSites(map[string]interface{}{"buildDrafts": true})
sites, err := cc.buildSites(map[string]any{"buildDrafts": true})
if err != nil {
return newSystemError("Error building sites", err)
}
@@ -94,7 +94,11 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.",
Short: "List all posts dated in the future",
Long: `List all of the posts in your content directory which will be posted in the future.`,
RunE: func(cmd *cobra.Command, args []string) error {
sites, err := cc.buildSites(map[string]interface{}{"buildFuture": true})
sites, err := cc.buildSites(map[string]any{"buildFuture": true})
if err != nil {
return newSystemError("Error building sites", err)
}
if err != nil {
return newSystemError("Error building sites", err)
}
@@ -122,7 +126,11 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.",
Short: "List all posts already expired",
Long: `List all of the posts in your content directory which has already expired.`,
RunE: func(cmd *cobra.Command, args []string) error {
sites, err := cc.buildSites(map[string]interface{}{"buildExpired": true})
sites, err := cc.buildSites(map[string]any{"buildExpired": true})
if err != nil {
return newSystemError("Error building sites", err)
}
if err != nil {
return newSystemError("Error building sites", err)
}
@@ -150,7 +158,7 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.",
Short: "List all posts",
Long: `List all of the posts in your content directory, include drafts, future and expired pages.`,
RunE: func(cmd *cobra.Command, args []string) error {
sites, err := cc.buildSites(map[string]interface{}{
sites, err := cc.buildSites(map[string]any{
"buildExpired": true,
"buildDrafts": true,
"buildFuture": true,
+3 -6
View File
@@ -29,17 +29,14 @@ func captureStdout(f func() error) (string, error) {
func TestListAll(t *testing.T) {
c := qt.New(t)
dir, clean, err := createSimpleTestSite(t, testSiteConfig{})
defer clean()
c.Assert(err, qt.IsNil)
dir := createSimpleTestSite(t, testSiteConfig{})
hugoCmd := newCommandsBuilder().addAll().build()
cmd := hugoCmd.getCommand()
defer func() {
t.Cleanup(func() {
os.RemoveAll(dir)
}()
})
cmd.SetArgs([]string{"-s=" + dir, "list", "all"})
+3 -1
View File
@@ -32,6 +32,7 @@ var _ cmder = (*newCmd)(nil)
type newCmd struct {
contentEditor string
contentType string
force bool
*baseBuilderCmd
}
@@ -54,6 +55,7 @@ Ensure you run this within the root directory of your site.`,
cmd.Flags().StringVarP(&cc.contentType, "kind", "k", "", "content type to create")
cmd.Flags().StringVar(&cc.contentEditor, "editor", "", "edit new content with this editor, if provided")
cmd.Flags().BoolVarP(&cc.force, "force", "f", false, "overwrite file if it already exists")
cmd.AddCommand(b.newNewSiteCmd().getCommand())
cmd.AddCommand(b.newNewThemeCmd().getCommand())
@@ -80,7 +82,7 @@ func (n *newCmd) newContent(cmd *cobra.Command, args []string) error {
return newUserError("path needs to be provided")
}
return create.NewContent(c.hugo(), n.contentType, args[0])
return create.NewContent(c.hugo(), n.contentType, args[0], n.force)
}
func mkdir(x ...string) {
+6 -5
View File
@@ -16,14 +16,13 @@ package commands
import (
"bytes"
"errors"
"fmt"
"path/filepath"
"strings"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/parser/metadecoders"
_errors "github.com/pkg/errors"
"github.com/gohugoio/hugo/create"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
@@ -94,7 +93,7 @@ func (n *newSiteCmd) doNewSite(fs *hugofs.Fs, basepath string, force bool) error
for _, dir := range dirs {
if err := fs.Source.MkdirAll(dir, 0777); err != nil {
return _errors.Wrap(err, "Failed to create dir")
return fmt.Errorf("Failed to create dir: %w", err)
}
}
@@ -122,8 +121,10 @@ func (n *newSiteCmd) newSite(cmd *cobra.Command, args []string) error {
}
forceNew, _ := cmd.Flags().GetBool("force")
return n.doNewSite(hugofs.NewDefault(config.New()), createpath, forceNew)
cfg := config.New()
cfg.Set("workingDir", createpath)
cfg.Set("publishDir", "public")
return n.doNewSite(hugofs.NewDefault(cfg), createpath, forceNew)
}
func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
+2 -2
View File
@@ -18,8 +18,8 @@ import (
"errors"
"path/filepath"
"strings"
"time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/spf13/cobra"
@@ -113,7 +113,7 @@ func (n *newThemeCmd) newTheme(cmd *cobra.Command, args []string) error {
by := []byte(`The MIT License (MIT)
Copyright (c) ` + time.Now().Format("2006") + ` YOUR_NAME_HERE
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
+1
View File
@@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build nodeploy
// +build nodeploy
package commands
+12 -12
View File
@@ -1,3 +1,4 @@
//go:build release
// +build release
// Copyright 2017-present The Hugo Authors. All rights reserved.
@@ -16,8 +17,6 @@
package commands
import (
"errors"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/releaser"
"github.com/spf13/cobra"
@@ -28,10 +27,9 @@ var _ cmder = (*releaseCommandeer)(nil)
type releaseCommandeer struct {
cmd *cobra.Command
version string
skipPublish bool
try bool
step int
skipPush bool
try bool
}
func createReleaser() cmder {
@@ -49,9 +47,9 @@ func createReleaser() cmder {
return r.release()
}
r.cmd.PersistentFlags().StringVarP(&r.version, "rel", "r", "", "new release version, i.e. 0.25.1")
r.cmd.PersistentFlags().BoolVarP(&r.skipPublish, "skip-publish", "", false, "skip all publishing pipes of the release")
r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "simulate a release, i.e. no changes")
r.cmd.PersistentFlags().BoolVarP(&r.skipPush, "skip-push", "", false, "skip pushing to remote")
r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "no changes")
r.cmd.PersistentFlags().IntVarP(&r.step, "step", "", 0, "step to run (1: set new version 2: prepare next dev version)")
return r
}
@@ -64,8 +62,10 @@ func (c *releaseCommandeer) flagsToConfig(cfg config.Provider) {
}
func (r *releaseCommandeer) release() error {
if r.version == "" {
return errors.New("must set the --rel flag to the relevant version number")
rel, err := releaser.New(r.skipPush, r.try, r.step)
if err != nil {
return err
}
return releaser.New(r.version, r.skipPublish, r.try).Run()
return rel.Run()
}
+1
View File
@@ -1,3 +1,4 @@
//go:build !release
// +build !release
// Copyright 2018 The Hugo Authors. All rights reserved.
+209 -81
View File
@@ -15,6 +15,7 @@ package commands
import (
"bytes"
"context"
"fmt"
"io"
"net"
@@ -22,6 +23,7 @@ import (
"net/url"
"os"
"os/signal"
"path"
"path/filepath"
"regexp"
"runtime"
@@ -31,9 +33,11 @@ import (
"syscall"
"time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/paths"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/tpl"
"golang.org/x/sync/errgroup"
"github.com/gohugoio/hugo/livereload"
@@ -46,17 +50,18 @@ import (
type serverCmd struct {
// Can be used to stop the server. Useful in tests
stop <-chan bool
stop chan bool
disableLiveReload bool
navigateToChanged bool
renderToDisk bool
serverAppend bool
serverInterface string
serverPort int
liveReloadPort int
serverWatch bool
noHTTPCache bool
disableLiveReload bool
navigateToChanged bool
renderToDisk bool
renderStaticToDisk bool
serverAppend bool
serverInterface string
serverPort int
liveReloadPort int
serverWatch bool
noHTTPCache bool
disableFastRender bool
disableBrowserError bool
@@ -68,7 +73,7 @@ func (b *commandsBuilder) newServerCmd() *serverCmd {
return b.newServerCmdSignaled(nil)
}
func (b *commandsBuilder) newServerCmdSignaled(stop <-chan bool) *serverCmd {
func (b *commandsBuilder) newServerCmdSignaled(stop chan bool) *serverCmd {
cc := &serverCmd{stop: stop}
cc.baseBuilderCmd = b.newBuilderCmd(&cobra.Command{
@@ -87,7 +92,13 @@ By default hugo will also watch your files for any changes you make and
automatically rebuild the site. It will then live reload any open browser pages
and push the latest content to them. As most Hugo sites are built in a fraction
of a second, you will be able to save and see your changes nearly instantly.`,
RunE: cc.server,
RunE: func(cmd *cobra.Command, args []string) error {
err := cc.server(cmd, args)
if err != nil && cc.stop != nil {
cc.stop <- true
}
return err
},
})
cc.cmd.Flags().IntVarP(&cc.serverPort, "port", "p", 1313, "port on which the server will listen")
@@ -98,7 +109,8 @@ of a second, you will be able to save and see your changes nearly instantly.`,
cc.cmd.Flags().BoolVarP(&cc.serverAppend, "appendPort", "", true, "append port to baseURL")
cc.cmd.Flags().BoolVar(&cc.disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild")
cc.cmd.Flags().BoolVar(&cc.navigateToChanged, "navigateToChanged", false, "navigate to changed content file on live browser reload")
cc.cmd.Flags().BoolVar(&cc.renderToDisk, "renderToDisk", false, "render to Destination path (default is render to memory & serve from there)")
cc.cmd.Flags().BoolVar(&cc.renderToDisk, "renderToDisk", false, "serve all files from disk (default is from memory)")
cc.cmd.Flags().BoolVar(&cc.renderStaticToDisk, "renderStaticToDisk", false, "serve static files from disk and dynamic files from memory")
cc.cmd.Flags().BoolVar(&cc.disableFastRender, "disableFastRender", false, "enables full re-renders on changes")
cc.cmd.Flags().BoolVar(&cc.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser")
@@ -128,8 +140,6 @@ func (f noDirFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, nil
}
var serverPorts []int
func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
// If a Destination is provided via flag write to disk
destination, _ := cmd.Flags().GetString("destination")
@@ -139,8 +149,9 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
var serverCfgInit sync.Once
cfgInit := func(c *commandeer) error {
c.Set("renderToMemory", !sc.renderToDisk)
cfgInit := func(c *commandeer) (rerr error) {
c.Set("renderToMemory", !(sc.renderToDisk || sc.renderStaticToDisk))
c.Set("renderStaticToDisk", sc.renderStaticToDisk)
if cmd.Flags().Changed("navigateToChanged") {
c.Set("navigateToChanged", sc.navigateToChanged)
}
@@ -157,64 +168,68 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
c.Set("watch", true)
}
// TODO(bep) yes, we should fix.
if !c.languagesConfigured {
// TODO(bep) see issue 9901
// cfgInit is called twice, before and after the languages have been initialized.
// The servers (below) can not be initialized before we
// know if we're configured in a multihost setup.
if len(c.languages) == 0 {
return nil
}
var err error
// We can only do this once.
serverCfgInit.Do(func() {
serverPorts = make([]int, 1)
c.serverPorts = make([]serverPortListener, 1)
if c.languages.IsMultihost() {
if !sc.serverAppend {
err = newSystemError("--appendPort=false not supported when in multihost mode")
rerr = newSystemError("--appendPort=false not supported when in multihost mode")
}
serverPorts = make([]int, len(c.languages))
c.serverPorts = make([]serverPortListener, len(c.languages))
}
currentServerPort := sc.serverPort
for i := 0; i < len(serverPorts); i++ {
for i := 0; i < len(c.serverPorts); i++ {
l, err := net.Listen("tcp", net.JoinHostPort(sc.serverInterface, strconv.Itoa(currentServerPort)))
if err == nil {
l.Close()
serverPorts[i] = currentServerPort
c.serverPorts[i] = serverPortListener{ln: l, p: currentServerPort}
} else {
if i == 0 && sc.cmd.Flags().Changed("port") {
// port set explicitly by user -- he/she probably meant it!
err = newSystemErrorF("Server startup failed: %s", err)
rerr = newSystemErrorF("Server startup failed: %s", err)
return
}
c.logger.Println("port", sc.serverPort, "already in use, attempting to use an available port")
sp, err := helpers.FindAvailablePort()
l, sp, err := helpers.TCPListen()
if err != nil {
err = newSystemError("Unable to find alternative port to use:", err)
rerr = newSystemError("Unable to find alternative port to use:", err)
return
}
serverPorts[i] = sp.Port
c.serverPorts[i] = serverPortListener{ln: l, p: sp.Port}
}
currentServerPort = serverPorts[i] + 1
currentServerPort = c.serverPorts[i].p + 1
}
})
c.serverPorts = serverPorts
if rerr != nil {
return
}
c.Set("port", sc.serverPort)
if sc.liveReloadPort != -1 {
c.Set("liveReloadPort", sc.liveReloadPort)
} else {
c.Set("liveReloadPort", serverPorts[0])
c.Set("liveReloadPort", c.serverPorts[0].p)
}
isMultiHost := c.languages.IsMultihost()
for i, language := range c.languages {
var serverPort int
if isMultiHost {
serverPort = serverPorts[i]
serverPort = c.serverPorts[i].p
} else {
serverPort = serverPorts[0]
serverPort = c.serverPorts[0].p
}
baseURL, err := sc.fixURL(language, sc.baseURL, serverPort)
@@ -229,7 +244,7 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
}
}
return err
return
}
if err := memStats(); err != nil {
@@ -257,10 +272,6 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
return err
}
for _, s := range c.hugo().Sites {
s.RegisterMediaTypes()
}
// Watch runs its own server as part of the routine
if sc.serverWatch {
@@ -298,7 +309,7 @@ func getRootWatchDirsStr(baseDir string, watchDirs []string) string {
type fileServer struct {
baseURLs []string
roots []string
errorTemplate func(err interface{}) (io.Reader, error)
errorTemplate func(err any) (io.Reader, error)
c *commandeer
s *serverCmd
}
@@ -314,31 +325,39 @@ func (f *fileServer) rewriteRequest(r *http.Request, toPath string) *http.Reques
return r2
}
func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, error) {
func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string, string, error) {
baseURL := f.baseURLs[i]
root := f.roots[i]
port := f.c.serverPorts[i]
port := f.c.serverPorts[i].p
listener := f.c.serverPorts[i].ln
// For logging only.
// TODO(bep) consolidate.
publishDir := f.c.Cfg.GetString("publishDir")
publishDirStatic := f.c.Cfg.GetString("publishDirStatic")
workingDir := f.c.Cfg.GetString("workingDir")
if root != "" {
publishDir = filepath.Join(publishDir, root)
publishDirStatic = filepath.Join(publishDirStatic, root)
}
absPublishDir := f.c.hugo().PathSpec.AbsPathify(publishDir)
absPublishDir := paths.AbsPathify(workingDir, publishDir)
absPublishDirStatic := paths.AbsPathify(workingDir, publishDirStatic)
jww.FEEDBACK.Printf("Environment: %q", f.c.hugo().Deps.Site.Hugo().Environment)
if i == 0 {
if f.s.renderToDisk {
jww.FEEDBACK.Println("Serving pages from " + absPublishDir)
} else if f.s.renderStaticToDisk {
jww.FEEDBACK.Println("Serving pages from memory and static files from " + absPublishDirStatic)
} else {
jww.FEEDBACK.Println("Serving pages from memory")
}
}
httpFs := afero.NewHttpFs(f.c.destinationFs)
fs := filesOnlyFs{httpFs.Dir(absPublishDir)}
httpFs := afero.NewHttpFs(f.c.publishDirServerFs)
fs := filesOnlyFs{httpFs.Dir(path.Join("/", root))}
if i == 0 && f.c.fastRenderMode {
jww.FEEDBACK.Println("Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender")
@@ -347,7 +366,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
// We're only interested in the path
u, err := url.Parse(baseURL)
if err != nil {
return nil, "", "", errors.Wrap(err, "Invalid baseURL")
return nil, nil, "", "", fmt.Errorf("Invalid baseURL: %w", err)
}
decorate := func(h http.Handler) http.Handler {
@@ -381,24 +400,31 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
}
// Ignore any query params for the operations below.
requestURI := strings.TrimSuffix(r.RequestURI, "?"+r.URL.RawQuery)
requestURI, _ := url.PathUnescape(strings.TrimSuffix(r.RequestURI, "?"+r.URL.RawQuery))
for _, header := range f.c.serverConfig.MatchHeaders(requestURI) {
w.Header().Set(header.Key, header.Value)
}
if redirect := f.c.serverConfig.MatchRedirect(requestURI); !redirect.IsZero() {
// fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
doRedirect := true
// This matches Netlify's behaviour and is needed for SPA behaviour.
// See https://docs.netlify.com/routing/redirects/rewrites-proxies/
if !redirect.Force {
path := filepath.Clean(strings.TrimPrefix(requestURI, u.Path))
fi, err := f.c.hugo().BaseFs.PublishFs.Stat(path)
if root != "" {
path = filepath.Join(root, path)
}
fs := f.c.publishDirServerFs
fi, err := fs.Stat(path)
if err == nil {
if fi.IsDir() {
// There will be overlapping directories, so we
// need to check for a file.
_, err = f.c.hugo().BaseFs.PublishFs.Stat(filepath.Join(path, "index.html"))
_, err = fs.Stat(filepath.Join(path, "index.html"))
doRedirect = err != nil
} else {
doRedirect = false
@@ -407,15 +433,28 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
}
if doRedirect {
if redirect.Status == 200 {
switch redirect.Status {
case 404:
w.WriteHeader(404)
file, err := fs.Open(strings.TrimPrefix(redirect.To, u.Path))
if err == nil {
defer file.Close()
io.Copy(w, file)
} else {
fmt.Fprintln(w, "<h1>Page Not Found</h1>")
}
return
case 200:
if r2 := f.rewriteRequest(r, strings.TrimPrefix(redirect.To, u.Path)); r2 != nil {
requestURI = redirect.To
r = r2
}
} else {
fallthrough
default:
w.Header().Set("Content-Type", "")
http.Redirect(w, r, redirect.To, redirect.Status)
return
}
}
@@ -453,15 +492,43 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
endpoint := net.JoinHostPort(f.s.serverInterface, strconv.Itoa(port))
return mu, u.String(), endpoint, nil
return mu, listener, u.String(), endpoint, nil
}
var logErrorRe = regexp.MustCompile(`(?s)ERROR \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} `)
var (
logErrorRe = regexp.MustCompile(`(?s)ERROR \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} `)
logDuplicateTemplateExecuteRe = regexp.MustCompile(`: template: .*?:\d+:\d+: executing ".*?"`)
logDuplicateTemplateParseRe = regexp.MustCompile(`: template: .*?:\d+:\d*`)
)
func removeErrorPrefixFromLog(content string) string {
return logErrorRe.ReplaceAllLiteralString(content, "")
}
var logReplacer = strings.NewReplacer(
"can't", "cant", // Chroma lexer does'nt do well with "can't"
"*hugolib.pageState", "page.Page", // Page is the public interface.
"Rebuild failed:", "",
)
func cleanErrorLog(content string) string {
content = strings.ReplaceAll(content, "\n", " ")
content = logReplacer.Replace(content)
content = logDuplicateTemplateExecuteRe.ReplaceAllString(content, "")
content = logDuplicateTemplateParseRe.ReplaceAllString(content, "")
seen := make(map[string]bool)
parts := strings.Split(content, ": ")
keep := make([]string, 0, len(parts))
for _, part := range parts {
if seen[part] {
continue
}
seen[part] = true
keep = append(keep, part)
}
return strings.Join(keep, ": ")
}
func (c *commandeer) serve(s *serverCmd) error {
isMultiHost := c.hugo().IsMultihost()
@@ -481,19 +548,37 @@ func (c *commandeer) serve(s *serverCmd) error {
roots = []string{""}
}
templ, err := c.hugo().TextTmpl().Parse("__default_server_error", buildErrorTemplate)
if err != nil {
return err
// Cache it here. The HugoSites object may be unavaialble later on due to intermitent configuration errors.
// To allow the en user to change the error template while the server is running, we use
// the freshest template we can provide.
var (
errTempl tpl.Template
templHandler tpl.TemplateHandler
)
getErrorTemplateAndHandler := func(h *hugolib.HugoSites) (tpl.Template, tpl.TemplateHandler) {
if h == nil {
return errTempl, templHandler
}
templHandler := h.Tmpl()
errTempl, found := templHandler.Lookup("_server/error.html")
if !found {
panic("template server/error.html not found")
}
return errTempl, templHandler
}
errTempl, templHandler = getErrorTemplateAndHandler(c.hugo())
srv := &fileServer{
baseURLs: baseURLs,
roots: roots,
c: c,
s: s,
errorTemplate: func(ctx interface{}) (io.Reader, error) {
errorTemplate: func(ctx any) (io.Reader, error) {
// hugoTry does not block, getErrorTemplateAndHandler will fall back
// to cached values if nil.
templ, handler := getErrorTemplateAndHandler(c.hugoTry())
b := &bytes.Buffer{}
err := c.hugo().Tmpl().Execute(templ, b, ctx)
err := handler.Execute(templ, b, ctx)
return b, err
},
}
@@ -506,9 +591,17 @@ func (c *commandeer) serve(s *serverCmd) error {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
var servers []*http.Server
wg1, ctx := errgroup.WithContext(context.Background())
for i := range baseURLs {
mu, serverURL, endpoint, err := srv.createEndpoint(i)
mu, listener, serverURL, endpoint, err := srv.createEndpoint(i)
srv := &http.Server{
Addr: endpoint,
Handler: mu,
}
servers = append(servers, srv)
if doLiveReload {
u, err := url.Parse(helpers.SanitizeURL(baseURLs[i]))
@@ -520,29 +613,64 @@ func (c *commandeer) serve(s *serverCmd) error {
mu.HandleFunc(u.Path+"/livereload", livereload.Handler)
}
jww.FEEDBACK.Printf("Web Server is available at %s (bind address %s)\n", serverURL, s.serverInterface)
go func() {
err = http.ListenAndServe(endpoint, mu)
if err != nil {
c.logger.Errorf("Error: %s\n", err.Error())
os.Exit(1)
wg1.Go(func() error {
err = srv.Serve(listener)
if err != nil && err != http.ErrServerClosed {
return err
}
}()
return nil
})
}
jww.FEEDBACK.Println("Press Ctrl+C to stop")
if s.stop != nil {
select {
case <-sigs:
case <-s.stop:
err := func() error {
if s.stop != nil {
for {
select {
case <-sigs:
return nil
case <-s.stop:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
} else {
for {
select {
case <-sigs:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
}
} else {
<-sigs
}()
if err != nil {
jww.ERROR.Println("Error:", err)
}
c.hugo().Close()
if h := c.hugoTry(); h != nil {
h.Close()
}
return nil
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
wg2, ctx := errgroup.WithContext(ctx)
for _, srv := range servers {
srv := srv
wg2.Go(func() error {
return srv.Shutdown(ctx)
})
}
err1, err2 := wg1.Wait(), wg2.Wait()
if err1 != nil {
return err1
}
return err2
}
// fixURL massages the baseURL into a form needed for serving
@@ -586,7 +714,7 @@ func (sc *serverCmd) fixURL(cfg config.Provider, s string, port int) (string, er
if strings.Contains(u.Host, ":") {
u.Host, _, err = net.SplitHostPort(u.Host)
if err != nil {
return "", errors.Wrap(err, "Failed to split baseURL hostpost")
return "", fmt.Errorf("Failed to split baseURL hostpost: %w", err)
}
}
u.Host += fmt.Sprintf(":%d", port)
@@ -615,13 +743,13 @@ func memStats() error {
go func() {
var stats runtime.MemStats
start := time.Now().UnixNano()
start := htime.Now().UnixNano()
for {
runtime.ReadMemStats(&stats)
if fileMemStats != nil {
fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n",
(time.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
(htime.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
time.Sleep(interval)
} else {
break
-61
View File
@@ -22,67 +22,6 @@ import (
"github.com/gohugoio/hugo/transform/livereloadinject"
)
var buildErrorTemplate = `<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<title>Hugo Server: Error</title>
<style type="text/css">
body {
font-family: "Muli",avenir, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 16px;
background-color: #2f1e2e;
}
main {
margin: auto;
width: 95%;
padding: 1rem;
}
.version {
color: #ccc;
padding: 1rem 0;
}
.stack {
margin-top: 4rem;
}
pre {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
.highlight {
overflow-x: auto;
margin-bottom: 1rem;
}
a {
color: #0594cb;
text-decoration: none;
}
a:hover {
color: #ccc;
}
</style>
</head>
<body>
<main>
{{ highlight .Error "apl" "linenos=false,noclasses=true,style=paraiso-dark" }}
{{ with .File }}
{{ $params := printf "noclasses=true,style=paraiso-dark,linenos=table,hl_lines=%d,linenostart=%d" (add .LinesPos 1) (sub .Position.LineNumber .LinesPos) }}
{{ $lexer := .ChromaLexer | default "go-html-template" }}
{{ highlight (delimit .Lines "\n") $lexer $params }}
{{ end }}
{{ with .StackTrace }}
{{ highlight . "apl" "noclasses=true,style=paraiso-dark" }}
{{ end }}
<p class="version">{{ .Version }}</p>
<a href="">Reload Page</a>
</main>
</body>
</html>
`
func injectLiveReloadScript(src io.Reader, baseURL url.URL) string {
var b bytes.Buffer
chain := transform.Chain{livereloadinject.New(baseURL)}
+323 -26
View File
@@ -17,6 +17,7 @@ import (
"fmt"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
@@ -24,22 +25,273 @@ import (
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/htesting"
"golang.org/x/net/context"
"golang.org/x/sync/errgroup"
qt "github.com/frankban/quicktest"
)
func TestServer(t *testing.T) {
if isWindowsCI() {
// TODO(bep) not sure why server tests have started to fail on the Windows CI server.
t.Skip("Skip server test on appveyor")
// Issue 9518
func TestServerPanicOnConfigError(t *testing.T) {
c := qt.New(t)
config := `
[markup]
[markup.highlight]
linenos='table'
`
r := runServerTest(c,
serverTestOptions{
config: config,
},
)
c.Assert(r.err, qt.IsNotNil)
c.Assert(r.err.Error(), qt.Contains, "cannot parse 'Highlight.LineNos' as bool:")
}
func TestServer404(t *testing.T) {
c := qt.New(t)
r := runServerTest(c,
serverTestOptions{
pathsToGet: []string{"this/does/not/exist"},
getNumHomes: 1,
},
)
c.Assert(r.err, qt.IsNil)
pr := r.pathsResults["this/does/not/exist"]
c.Assert(pr.statusCode, qt.Equals, http.StatusNotFound)
c.Assert(pr.body, qt.Contains, "404: 404 Page not found|Not Found.")
}
func TestServerPathEncodingIssues(t *testing.T) {
if htesting.IsGitHubAction() {
// This test is flaky on CI for some reason. Run it on Windows only for now.
// TODO(bep)
if runtime.GOOS != "windows" {
t.Skip("Skipping test on CI")
}
}
c := qt.New(t)
dir, clean, err := createSimpleTestSite(t, testSiteConfig{})
defer clean()
c.Assert(err, qt.IsNil)
// Let us hope that this port is available on all systems ...
port := 1331
// Issue 10287
c.Run("Unicode paths", func(c *qt.C) {
r := runServerTest(c,
serverTestOptions{
pathsToGet: []string{"hügö/"},
},
)
c.Assert(r.err, qt.IsNil)
c.Assert(r.pathsResults["hügö/"].body, qt.Contains, "This is hügö")
})
// Issue 10314
c.Run("Windows multilingual 404", func(c *qt.C) {
config := `
baseURL = 'https://example.org/'
title = 'Hugo Forum Topic #40568'
defaultContentLanguageInSubdir = true
[languages.en]
contentDir = 'content/en'
languageCode = 'en-US'
languageName = 'English'
weight = 1
[languages.es]
contentDir = 'content/es'
languageCode = 'es-ES'
languageName = 'Espanol'
weight = 2
[server]
[[server.redirects]]
from = '/en/**'
to = '/en/404.html'
status = 404
[[server.redirects]]
from = '/es/**'
to = '/es/404.html'
status = 404
`
r := runServerTest(c,
serverTestOptions{
config: config,
pathsToGet: []string{"en/this/does/not/exist", "es/this/does/not/exist"},
},
)
c.Assert(r.err, qt.IsNil)
pr1 := r.pathsResults["en/this/does/not/exist"]
pr2 := r.pathsResults["es/this/does/not/exist"]
c.Assert(pr1.statusCode, qt.Equals, http.StatusNotFound)
c.Assert(pr2.statusCode, qt.Equals, http.StatusNotFound)
c.Assert(pr1.body, qt.Contains, "404: 404 Page not found|Not Found.")
c.Assert(pr2.body, qt.Contains, "404: 404 Page not found|Not Found.")
})
}
func TestServerFlags(t *testing.T) {
c := qt.New(t)
assertPublic := func(c *qt.C, r serverTestResult, renderStaticToDisk bool) {
c.Assert(r.err, qt.IsNil)
c.Assert(r.homesContent[0], qt.Contains, "Environment: development")
c.Assert(r.publicDirnames["myfile.txt"], qt.Equals, renderStaticToDisk)
}
for _, test := range []struct {
flag string
assert func(c *qt.C, r serverTestResult)
}{
{"", func(c *qt.C, r serverTestResult) {
assertPublic(c, r, false)
}},
{"--renderToDisk", func(c *qt.C, r serverTestResult) {
assertPublic(c, r, true)
}},
{"--renderStaticToDisk", func(c *qt.C, r serverTestResult) {
assertPublic(c, r, true)
}},
} {
c.Run(test.flag, func(c *qt.C) {
config := `
baseURL="https://example.org"
`
var args []string
if test.flag != "" {
args = strings.Split(test.flag, "=")
}
opts := serverTestOptions{
config: config,
args: args,
getNumHomes: 1,
}
r := runServerTest(c, opts)
test.assert(c, r)
})
}
}
func TestServerBugs(t *testing.T) {
c := qt.New(t)
for _, test := range []struct {
name string
config string
flag string
numservers int
assert func(c *qt.C, r serverTestResult)
}{
{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
c.Assert(r.err, qt.IsNil)
c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
}},
// Issue 9788
{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
c.Assert(r.err, qt.IsNil)
c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
}},
{"PostProcess, disk", "", "--renderToDisk", 1, func(c *qt.C, r serverTestResult) {
c.Assert(r.err, qt.IsNil)
c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
}},
// Isue 9901
{"Multihost", `
defaultContentLanguage = 'en'
[languages]
[languages.en]
baseURL = 'https://example.com'
title = 'My blog'
weight = 1
[languages.fr]
baseURL = 'https://example.fr'
title = 'Mon blogue'
weight = 2
`, "", 2, func(c *qt.C, r serverTestResult) {
c.Assert(r.err, qt.IsNil)
for i, s := range []string{"My blog", "Mon blogue"} {
c.Assert(r.homesContent[i], qt.Contains, s)
}
}},
} {
c.Run(test.name, func(c *qt.C) {
if test.config == "" {
test.config = `
baseURL="https://example.org"
`
}
var args []string
if test.flag != "" {
args = strings.Split(test.flag, "=")
}
opts := serverTestOptions{
config: test.config,
getNumHomes: test.numservers,
pathsToGet: []string{"this/does/not/exist"},
args: args,
}
r := runServerTest(c, opts)
pr := r.pathsResults["this/does/not/exist"]
c.Assert(pr.statusCode, qt.Equals, http.StatusNotFound)
c.Assert(pr.body, qt.Contains, "404: 404 Page not found|Not Found.")
test.assert(c, r)
})
}
}
type serverTestResult struct {
err error
homesContent []string
content404 string
publicDirnames map[string]bool
pathsResults map[string]pathResult
}
type pathResult struct {
statusCode int
body string
}
type serverTestOptions struct {
getNumHomes int
config string
pathsToGet []string
args []string
}
func runServerTest(c *qt.C, opts serverTestOptions) serverTestResult {
dir := createSimpleTestSite(c, testSiteConfig{configTOML: opts.config})
result := serverTestResult{
publicDirnames: make(map[string]bool),
pathsResults: make(map[string]pathResult),
}
sp, err := helpers.FindAvailablePort()
c.Assert(err, qt.IsNil)
port := sp.Port
defer func() {
os.RemoveAll(dir)
@@ -51,28 +303,73 @@ func TestServer(t *testing.T) {
scmd := b.newServerCmdSignaled(stop)
cmd := scmd.getCommand()
cmd.SetArgs([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)})
args := append([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)}, opts.args...)
cmd.SetArgs(args)
go func() {
_, err = cmd.ExecuteC()
c.Assert(err, qt.IsNil)
}()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
wg, ctx := errgroup.WithContext(ctx)
// There is no way to know exactly when the server is ready for connections.
// We could improve by something like https://golang.org/pkg/net/http/httptest/#Server
// But for now, let us sleep and pray!
time.Sleep(2 * time.Second)
wg.Go(func() error {
_, err := cmd.ExecuteC()
return err
})
resp, err := http.Get("http://localhost:1331/")
if opts.getNumHomes > 0 {
// Esp. on slow CI machines, we need to wait a little before the web
// server is ready.
wait := 567 * time.Millisecond
if os.Getenv("CI") != "" {
wait = 2 * time.Second
}
time.Sleep(wait)
result.homesContent = make([]string, opts.getNumHomes)
for i := 0; i < opts.getNumHomes; i++ {
func() {
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", port+i))
c.Assert(err, qt.IsNil)
c.Assert(resp.StatusCode, qt.Equals, http.StatusOK)
if err == nil {
defer resp.Body.Close()
result.homesContent[i] = helpers.ReaderToString(resp.Body)
}
}()
}
}
for _, path := range opts.pathsToGet {
func() {
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/%s", port, path))
c.Assert(err, qt.IsNil)
pr := pathResult{
statusCode: resp.StatusCode,
}
if err == nil {
defer resp.Body.Close()
pr.body = helpers.ReaderToString(resp.Body)
}
result.pathsResults[path] = pr
}()
}
time.Sleep(1 * time.Second)
select {
case <-stop:
case stop <- true:
}
pubFiles, err := os.ReadDir(filepath.Join(dir, "public"))
c.Assert(err, qt.IsNil)
defer resp.Body.Close()
homeContent := helpers.ReaderToString(resp.Body)
for _, f := range pubFiles {
result.publicDirnames[f.Name()] = true
}
c.Assert(homeContent, qt.Contains, "List: Hugo Commands")
c.Assert(homeContent, qt.Contains, "Environment: development")
result.err = wg.Wait()
return result
// Stop the server.
stop <- true
}
func TestFixURL(t *testing.T) {
@@ -101,7 +398,7 @@ func TestFixURL(t *testing.T) {
t.Run(test.TestName, func(t *testing.T) {
b := newCommandsBuilder()
s := b.newServerCmd()
v := config.New()
v := config.NewWithTestDefaults()
baseURL := test.CLIBaseURL
v.Set("baseURL", test.CfgBaseURL)
s.serverAppend = test.AppendPort
+8 -10
View File
@@ -40,11 +40,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
c := s.c
syncFn := func(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
publishDir := c.hugo().PathSpec.PublishDir
// If root, remove the second '/'
if publishDir == "//" {
publishDir = helpers.FilePathSeparator
}
publishDir := helpers.FilePathSeparator
if sourceFs.PublishFolder != "" {
publishDir = filepath.Join(publishDir, sourceFs.PublishFolder)
@@ -55,7 +51,10 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
syncer.NoChmod = c.Cfg.GetBool("noChmod")
syncer.ChmodFilter = chmodFilter
syncer.SrcFs = sourceFs.Fs
syncer.DestFs = c.Fs.Destination
syncer.DestFs = c.Fs.PublishDir
if c.renderStaticToDisk {
syncer.DestFs = c.Fs.PublishDirStatic
}
// prevent spamming the log on changes
logger := helpers.NewDistinctErrorLogger()
@@ -98,15 +97,14 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
if ev.Op&fsnotify.Rename == fsnotify.Rename || ev.Op&fsnotify.Remove == fsnotify.Remove {
if _, err := sourceFs.Fs.Stat(relPath); os.IsNotExist(err) {
// If file doesn't exist in any static dir, remove it
toRemove := filepath.Join(publishDir, relPath)
logger.Println("File no longer exists in static dir, removing", relPath)
_ = c.Fs.PublishDirStatic.RemoveAll(relPath)
logger.Println("File no longer exists in static dir, removing", toRemove)
_ = c.Fs.Destination.RemoveAll(toRemove)
} else if err == nil {
// If file still exists, sync it
logger.Println("Syncing", relPath, "to", publishDir)
if err := syncer.Sync(filepath.Join(publishDir, relPath), relPath); err != nil {
if err := syncer.Sync(relPath, relPath); err != nil {
c.logger.Errorln(err)
}
} else {
+5 -5
View File
@@ -21,7 +21,7 @@ import (
// Append appends from to a slice to and returns the resulting slice.
// If length of from is one and the only element is a slice of same type as to,
// it will be appended.
func Append(to interface{}, from ...interface{}) (interface{}, error) {
func Append(to any, from ...any) (any, error) {
tov, toIsNil := indirect(reflect.ValueOf(to))
toIsNil = toIsNil || to == nil
@@ -73,8 +73,8 @@ func Append(to interface{}, from ...interface{}) (interface{}, error) {
return tov.Interface(), nil
}
func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]interface{}, error) {
var tos []interface{}
func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, error) {
var tos []any
for _, slice := range []reflect.Value{slice1, slice2} {
for i := 0; i < slice.Len(); i++ {
@@ -85,8 +85,8 @@ func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]interface
return tos, nil
}
func appendToInterfaceSlice(tov reflect.Value, from ...interface{}) ([]interface{}, error) {
var tos []interface{}
func appendToInterfaceSlice(tov reflect.Value, from ...any) ([]any, error) {
var tos []any
for i := 0; i < tov.Len(); i++ {
tos = append(tos, tov.Index(i).Interface())
+19 -19
View File
@@ -25,25 +25,25 @@ func TestAppend(t *testing.T) {
c := qt.New(t)
for _, test := range []struct {
start interface{}
addend []interface{}
expected interface{}
start any
addend []any
expected any
}{
{[]string{"a", "b"}, []interface{}{"c"}, []string{"a", "b", "c"}},
{[]string{"a", "b"}, []interface{}{"c", "d", "e"}, []string{"a", "b", "c", "d", "e"}},
{[]string{"a", "b"}, []interface{}{[]string{"c", "d", "e"}}, []string{"a", "b", "c", "d", "e"}},
{[]string{"a"}, []interface{}{"b", template.HTML("c")}, []interface{}{"a", "b", template.HTML("c")}},
{nil, []interface{}{"a", "b"}, []string{"a", "b"}},
{nil, []interface{}{nil}, []interface{}{nil}},
{[]interface{}{}, []interface{}{[]string{"c", "d", "e"}}, []string{"c", "d", "e"}},
{[]string{"a", "b"}, []any{"c"}, []string{"a", "b", "c"}},
{[]string{"a", "b"}, []any{"c", "d", "e"}, []string{"a", "b", "c", "d", "e"}},
{[]string{"a", "b"}, []any{[]string{"c", "d", "e"}}, []string{"a", "b", "c", "d", "e"}},
{[]string{"a"}, []any{"b", template.HTML("c")}, []any{"a", "b", template.HTML("c")}},
{nil, []any{"a", "b"}, []string{"a", "b"}},
{nil, []any{nil}, []any{nil}},
{[]any{}, []any{[]string{"c", "d", "e"}}, []string{"c", "d", "e"}},
{
tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}},
[]interface{}{&tstSlicer{"c"}},
[]any{&tstSlicer{"c"}},
tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}, &tstSlicer{"c"}},
},
{
&tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}},
[]interface{}{&tstSlicer{"c"}},
[]any{&tstSlicer{"c"}},
tstSlicers{
&tstSlicer{"a"},
&tstSlicer{"b"},
@@ -52,26 +52,26 @@ func TestAppend(t *testing.T) {
},
{
testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}},
[]interface{}{&tstSlicerIn1{"c"}},
[]any{&tstSlicerIn1{"c"}},
testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}, &tstSlicerIn1{"c"}},
},
//https://github.com/gohugoio/hugo/issues/5361
{
[]string{"a", "b"},
[]interface{}{tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}},
[]interface{}{"a", "b", &tstSlicer{"a"}, &tstSlicer{"b"}},
[]any{tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}},
[]any{"a", "b", &tstSlicer{"a"}, &tstSlicer{"b"}},
},
{
[]string{"a", "b"},
[]interface{}{&tstSlicer{"a"}},
[]interface{}{"a", "b", &tstSlicer{"a"}},
[]any{&tstSlicer{"a"}},
[]any{"a", "b", &tstSlicer{"a"}},
},
// Errors
{"", []interface{}{[]string{"a", "b"}}, false},
{"", []any{[]string{"a", "b"}}, false},
// No string concatenation.
{
"ab",
[]interface{}{"c"},
[]any{"c"},
false,
},
} {
+1 -1
View File
@@ -17,5 +17,5 @@ package collections
// Grouper defines a very generic way to group items by a given key.
type Grouper interface {
Group(key interface{}, items interface{}) (interface{}, error)
Group(key any, items any) (any, error)
}
+4 -4
View File
@@ -21,11 +21,11 @@ import (
// in collections.Slice template func to get types such as Pages, PageGroups etc.
// instead of the less useful []interface{}.
type Slicer interface {
Slice(items interface{}) (interface{}, error)
Slice(items any) (any, error)
}
// Slice returns a slice of all passed arguments.
func Slice(args ...interface{}) interface{} {
func Slice(args ...any) any {
if len(args) == 0 {
return args
}
@@ -66,8 +66,8 @@ func Slice(args ...interface{}) interface{} {
}
// StringSliceToInterfaceSlice converts ss to []interface{}.
func StringSliceToInterfaceSlice(ss []string) []interface{} {
result := make([]interface{}, len(ss))
func StringSliceToInterfaceSlice(ss []string) []any {
result := make([]any, len(ss))
for i, s := range ss {
result[i] = s
}
+16 -16
View File
@@ -46,8 +46,8 @@ type tstSlicer struct {
TheName string
}
func (p *tstSlicerIn1) Slice(in interface{}) (interface{}, error) {
items := in.([]interface{})
func (p *tstSlicerIn1) Slice(in any) (any, error) {
items := in.([]any)
result := make(testSlicerInterfaces, len(items))
for i, v := range items {
switch vv := v.(type) {
@@ -60,8 +60,8 @@ func (p *tstSlicerIn1) Slice(in interface{}) (interface{}, error) {
return result, nil
}
func (p *tstSlicerIn2) Slice(in interface{}) (interface{}, error) {
items := in.([]interface{})
func (p *tstSlicerIn2) Slice(in any) (any, error) {
items := in.([]any)
result := make(testSlicerInterfaces, len(items))
for i, v := range items {
switch vv := v.(type) {
@@ -82,8 +82,8 @@ func (p *tstSlicerIn2) Name() string {
return p.TheName
}
func (p *tstSlicer) Slice(in interface{}) (interface{}, error) {
items := in.([]interface{})
func (p *tstSlicer) Slice(in any) (any, error) {
items := in.([]any)
result := make(tstSlicers, len(items))
for i, v := range items {
switch vv := v.(type) {
@@ -103,17 +103,17 @@ func TestSlice(t *testing.T) {
c := qt.New(t)
for i, test := range []struct {
args []interface{}
expected interface{}
args []any
expected any
}{
{[]interface{}{"a", "b"}, []string{"a", "b"}},
{[]interface{}{&tstSlicer{"a"}, &tstSlicer{"b"}}, tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}},
{[]interface{}{&tstSlicer{"a"}, "b"}, []interface{}{&tstSlicer{"a"}, "b"}},
{[]interface{}{}, []interface{}{}},
{[]interface{}{nil}, []interface{}{nil}},
{[]interface{}{5, "b"}, []interface{}{5, "b"}},
{[]interface{}{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}, testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}},
{[]interface{}{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}, []interface{}{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}},
{[]any{"a", "b"}, []string{"a", "b"}},
{[]any{&tstSlicer{"a"}, &tstSlicer{"b"}}, tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}},
{[]any{&tstSlicer{"a"}, "b"}, []any{&tstSlicer{"a"}, "b"}},
{[]any{}, []any{}},
{[]any{nil}, []any{nil}},
{[]any{5, "b"}, []any{5, "b"}},
{[]any{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}, testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}},
{[]any{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}, []any{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}},
} {
errMsg := qt.Commentf("[%d] %v", i, test.args)
+43 -141
View File
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// 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.
@@ -21,8 +21,6 @@ import (
"strings"
"github.com/gohugoio/hugo/common/text"
"github.com/spf13/afero"
)
// LineMatcher contains the elements used to match an error to a line
@@ -36,14 +34,33 @@ type LineMatcher struct {
}
// LineMatcherFn is used to match a line with an error.
type LineMatcherFn func(m LineMatcher) bool
// It returns the column number or 0 if the line was found, but column could not be determinde. Returns -1 if no line match.
type LineMatcherFn func(m LineMatcher) int
// SimpleLineMatcher simply matches by line number.
var SimpleLineMatcher = func(m LineMatcher) bool {
return m.Position.LineNumber == m.LineNumber
var SimpleLineMatcher = func(m LineMatcher) int {
if m.Position.LineNumber == m.LineNumber {
// We found the line, but don't know the column.
return 0
}
return -1
}
var _ text.Positioner = ErrorContext{}
// NopLineMatcher is a matcher that always returns 1.
// This will effectively give line 1, column 1.
var NopLineMatcher = func(m LineMatcher) int {
return 1
}
// OffsetMatcher is a line matcher that matches by offset.
var OffsetMatcher = func(m LineMatcher) int {
if m.Offset+len(m.Line) >= m.Position.Offset {
// We found the line, but return 0 to signal that we want to determine
// the column from the error.
return 0
}
return -1
}
// ErrorContext contains contextual information about an error. This will
// typically be the lines surrounding some problem in a file.
@@ -56,125 +73,15 @@ type ErrorContext struct {
// The position of the error in the Lines above. 0 based.
LinesPos int
position text.Position
// The position of the content in the file. Note that this may be different from the error's position set
// in FileError.
Position text.Position
// The lexer to use for syntax highlighting.
// https://gohugo.io/content-management/syntax-highlighting/#list-of-chroma-highlighting-languages
ChromaLexer string
}
// Position returns the text position of this error.
func (e ErrorContext) Position() text.Position {
return e.position
}
var _ causer = (*ErrorWithFileContext)(nil)
// ErrorWithFileContext is an error with some additional file context related
// to that error.
type ErrorWithFileContext struct {
cause error
ErrorContext
}
func (e *ErrorWithFileContext) Error() string {
pos := e.Position()
if pos.IsValid() {
return pos.String() + ": " + e.cause.Error()
}
return e.cause.Error()
}
func (e *ErrorWithFileContext) Cause() error {
return e.cause
}
// WithFileContextForFile will try to add a file context with lines matching the given matcher.
// If no match could be found, the original error is returned with false as the second return value.
func WithFileContextForFile(e error, realFilename, filename string, fs afero.Fs, matcher LineMatcherFn) (error, bool) {
f, err := fs.Open(filename)
if err != nil {
return e, false
}
defer f.Close()
return WithFileContext(e, realFilename, f, matcher)
}
// WithFileContextForFileDefault tries to add file context using the default line matcher.
func WithFileContextForFileDefault(err error, filename string, fs afero.Fs) error {
err, _ = WithFileContextForFile(
err,
filename,
filename,
fs,
SimpleLineMatcher)
return err
}
// WithFileContextForFile will try to add a file context with lines matching the given matcher.
// If no match could be found, the original error is returned with false as the second return value.
func WithFileContext(e error, realFilename string, r io.Reader, matcher LineMatcherFn) (error, bool) {
if e == nil {
panic("error missing")
}
le := UnwrapFileError(e)
if le == nil {
var ok bool
if le, ok = ToFileError("", e).(FileError); !ok {
return e, false
}
}
var errCtx ErrorContext
posle := le.Position()
if posle.Offset != -1 {
errCtx = locateError(r, le, func(m LineMatcher) bool {
if posle.Offset >= m.Offset && posle.Offset < m.Offset+len(m.Line) {
lno := posle.LineNumber - m.Position.LineNumber + m.LineNumber
m.Position = text.Position{LineNumber: lno}
}
return matcher(m)
})
} else {
errCtx = locateError(r, le, matcher)
}
pos := &errCtx.position
if pos.LineNumber == -1 {
return e, false
}
pos.Filename = realFilename
if le.Type() != "" {
errCtx.ChromaLexer = chromaLexerFromType(le.Type())
} else {
errCtx.ChromaLexer = chromaLexerFromFilename(realFilename)
}
return &ErrorWithFileContext{cause: e, ErrorContext: errCtx}, true
}
// UnwrapErrorWithFileContext tries to unwrap an ErrorWithFileContext from err.
// It returns nil if this is not possible.
func UnwrapErrorWithFileContext(err error) *ErrorWithFileContext {
for err != nil {
switch v := err.(type) {
case *ErrorWithFileContext:
return v
case causer:
err = v.Cause()
default:
return nil
}
}
return nil
}
func chromaLexerFromType(fileType string) string {
switch fileType {
case "html", "htm":
@@ -196,31 +103,24 @@ func chromaLexerFromFilename(filename string) string {
return chromaLexerFromType(ext)
}
func locateErrorInString(src string, matcher LineMatcherFn) ErrorContext {
func locateErrorInString(src string, matcher LineMatcherFn) *ErrorContext {
return locateError(strings.NewReader(src), &fileError{}, matcher)
}
func locateError(r io.Reader, le FileError, matches LineMatcherFn) ErrorContext {
func locateError(r io.Reader, le FileError, matches LineMatcherFn) *ErrorContext {
if le == nil {
panic("must provide an error")
}
errCtx := ErrorContext{position: text.Position{LineNumber: -1, ColumnNumber: 1, Offset: -1}, LinesPos: -1}
ectx := &ErrorContext{LinesPos: -1, Position: text.Position{Offset: -1}}
b, err := ioutil.ReadAll(r)
if err != nil {
return errCtx
return ectx
}
pos := &errCtx.position
lepos := le.Position()
lines := strings.Split(string(b), "\n")
if lepos.ColumnNumber >= 0 {
pos.ColumnNumber = lepos.ColumnNumber
}
lineNo := 0
posBytes := 0
@@ -233,34 +133,36 @@ func locateError(r io.Reader, le FileError, matches LineMatcherFn) ErrorContext
Offset: posBytes,
Line: line,
}
if errCtx.LinesPos == -1 && matches(m) {
pos.LineNumber = lineNo
v := matches(m)
if ectx.LinesPos == -1 && v != -1 {
ectx.Position.LineNumber = lineNo
ectx.Position.ColumnNumber = v
break
}
posBytes += len(line)
}
if pos.LineNumber != -1 {
low := pos.LineNumber - 3
if ectx.Position.LineNumber > 0 {
low := ectx.Position.LineNumber - 3
if low < 0 {
low = 0
}
if pos.LineNumber > 2 {
errCtx.LinesPos = 2
if ectx.Position.LineNumber > 2 {
ectx.LinesPos = 2
} else {
errCtx.LinesPos = pos.LineNumber - 1
ectx.LinesPos = ectx.Position.LineNumber - 1
}
high := pos.LineNumber + 2
high := ectx.Position.LineNumber + 2
if high > len(lines) {
high = len(lines)
}
errCtx.Lines = lines[low:high]
ectx.Lines = lines[low:high]
}
return errCtx
return ectx
}
+43 -19
View File
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// 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.
@@ -24,8 +24,11 @@ import (
func TestErrorLocator(t *testing.T) {
c := qt.New(t)
lineMatcher := func(m LineMatcher) bool {
return strings.Contains(m.Line, "THEONE")
lineMatcher := func(m LineMatcher) int {
if strings.Contains(m.Line, "THEONE") {
return 1
}
return -1
}
lines := `LINE 1
@@ -39,35 +42,41 @@ LINE 8
`
location := locateErrorInString(lines, lineMatcher)
pos := location.Position
c.Assert(location.Lines, qt.DeepEquals, []string{"LINE 3", "LINE 4", "This is THEONE", "LINE 6", "LINE 7"})
pos := location.Position()
c.Assert(pos.LineNumber, qt.Equals, 5)
c.Assert(location.LinesPos, qt.Equals, 2)
c.Assert(locateErrorInString(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"})
locate := func(s string, m LineMatcherFn) *ErrorContext {
ctx := locateErrorInString(s, m)
return ctx
}
c.Assert(locate(`This is THEONE`, lineMatcher).Lines, qt.DeepEquals, []string{"This is THEONE"})
location = locateErrorInString(`L1
This is THEONE
L2
`, lineMatcher)
c.Assert(location.Position().LineNumber, qt.Equals, 2)
pos = location.Position
c.Assert(pos.LineNumber, qt.Equals, 2)
c.Assert(location.LinesPos, qt.Equals, 1)
c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This is THEONE", "L2", ""})
location = locateErrorInString(`This is THEONE
location = locate(`This is THEONE
L2
`, lineMatcher)
c.Assert(location.LinesPos, qt.Equals, 0)
c.Assert(location.Lines, qt.DeepEquals, []string{"This is THEONE", "L2", ""})
location = locateErrorInString(`L1
location = locate(`L1
This THEONE
`, lineMatcher)
c.Assert(location.Lines, qt.DeepEquals, []string{"L1", "This THEONE", ""})
c.Assert(location.LinesPos, qt.Equals, 1)
location = locateErrorInString(`L1
location = locate(`L1
L2
This THEONE
`, lineMatcher)
@@ -75,12 +84,16 @@ This THEONE
c.Assert(location.LinesPos, qt.Equals, 2)
location = locateErrorInString("NO MATCH", lineMatcher)
c.Assert(location.Position().LineNumber, qt.Equals, -1)
pos = location.Position
c.Assert(pos.LineNumber, qt.Equals, 0)
c.Assert(location.LinesPos, qt.Equals, -1)
c.Assert(len(location.Lines), qt.Equals, 0)
lineMatcher = func(m LineMatcher) bool {
return m.LineNumber == 6
lineMatcher = func(m LineMatcher) int {
if m.LineNumber == 6 {
return 1
}
return -1
}
location = locateErrorInString(`A
@@ -93,14 +106,18 @@ G
H
I
J`, lineMatcher)
pos = location.Position
c.Assert(location.Lines, qt.DeepEquals, []string{"D", "E", "F", "G", "H"})
c.Assert(location.Position().LineNumber, qt.Equals, 6)
c.Assert(pos.LineNumber, qt.Equals, 6)
c.Assert(location.LinesPos, qt.Equals, 2)
// Test match EOF
lineMatcher = func(m LineMatcher) bool {
return m.LineNumber == 4
lineMatcher = func(m LineMatcher) int {
if m.LineNumber == 4 {
return 1
}
return -1
}
location = locateErrorInString(`A
@@ -108,12 +125,17 @@ B
C
`, lineMatcher)
pos = location.Position
c.Assert(location.Lines, qt.DeepEquals, []string{"B", "C", ""})
c.Assert(location.Position().LineNumber, qt.Equals, 4)
c.Assert(pos.LineNumber, qt.Equals, 4)
c.Assert(location.LinesPos, qt.Equals, 2)
offsetMatcher := func(m LineMatcher) bool {
return m.Offset == 1
offsetMatcher := func(m LineMatcher) int {
if m.Offset == 1 {
return 1
}
return -1
}
location = locateErrorInString(`A
@@ -122,7 +144,9 @@ C
D
E`, offsetMatcher)
pos = location.Position
c.Assert(location.Lines, qt.DeepEquals, []string{"A", "B", "C", "D"})
c.Assert(location.Position().LineNumber, qt.Equals, 2)
c.Assert(pos.LineNumber, qt.Equals, 2)
c.Assert(location.LinesPos, qt.Equals, 1)
}
+1 -27
View File
@@ -19,37 +19,11 @@ import (
"errors"
"fmt"
"io"
"os"
"runtime"
"runtime/debug"
"strconv"
_errors "github.com/pkg/errors"
)
// As defined in https://godoc.org/github.com/pkg/errors
type causer interface {
Cause() error
}
type stackTracer interface {
StackTrace() _errors.StackTrace
}
// PrintStackTraceFromErr prints the error's stack trace to stdoud.
func PrintStackTraceFromErr(err error) {
FprintStackTraceFromErr(os.Stdout, err)
}
// FprintStackTraceFromErr prints the error's stack trace to w.
func FprintStackTraceFromErr(w io.Writer, err error) {
if err, ok := err.(stackTracer); ok {
for _, f := range err.StackTrace() {
fmt.Fprintf(w, "%+s:%d\n", f, f)
}
}
}
// PrintStackTrace prints the current stacktrace to w.
func PrintStackTrace(w io.Writer) {
buf := make([]byte, 1<<16)
@@ -65,7 +39,7 @@ type ErrorSender interface {
// Recover is a helper function that can be used to capture panics.
// Put this at the top of a method/function that crashes in a template:
// defer herrors.Recover()
func Recover(args ...interface{}) {
func Recover(args ...any) {
if r := recover(); r != nil {
fmt.Println("ERR:", r)
args = append(args, "stacktrace from panic: \n"+string(debug.Stack()), "\n")
+313 -50
View File
@@ -1,11 +1,11 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// 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
// Unless required by applicable lfmtaw 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
@@ -15,61 +15,306 @@ package herrors
import (
"encoding/json"
"fmt"
"io"
"path/filepath"
"github.com/bep/godartsass"
"github.com/bep/golibsass/libsass/libsasserrors"
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/text"
"github.com/pelletier/go-toml/v2"
"github.com/spf13/afero"
"github.com/tdewolff/parse/v2"
"github.com/pkg/errors"
"errors"
)
var _ causer = (*fileError)(nil)
// FileError represents an error when handling a file: Parsing a config file,
// execute a template etc.
type FileError interface {
error
// ErroContext holds some context information about the error.
ErrorContext() *ErrorContext
text.Positioner
// A string identifying the type of file, e.g. JSON, TOML, markdown etc.
Type() string
// UpdatePosition updates the position of the error.
UpdatePosition(pos text.Position) FileError
// UpdateContent updates the error with a new ErrorContext from the content of the file.
UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError
}
var _ FileError = (*fileError)(nil)
// Unwrapper can unwrap errors created with fmt.Errorf.
type Unwrapper interface {
Unwrap() error
}
var (
_ FileError = (*fileError)(nil)
_ Unwrapper = (*fileError)(nil)
)
func (fe *fileError) UpdatePosition(pos text.Position) FileError {
oldFilename := fe.Position().Filename
if pos.Filename != "" && fe.fileType == "" {
_, fe.fileType = paths.FileAndExtNoDelimiter(filepath.Clean(pos.Filename))
}
if pos.Filename == "" {
pos.Filename = oldFilename
}
fe.position = pos
return fe
}
func (fe *fileError) UpdateContent(r io.Reader, linematcher LineMatcherFn) FileError {
if linematcher == nil {
linematcher = SimpleLineMatcher
}
var (
posle = fe.position
ectx *ErrorContext
)
if posle.LineNumber <= 1 && posle.Offset > 0 {
// Try to locate the line number from the content if offset is set.
ectx = locateError(r, fe, func(m LineMatcher) int {
if posle.Offset >= m.Offset && posle.Offset < m.Offset+len(m.Line) {
lno := posle.LineNumber - m.Position.LineNumber + m.LineNumber
m.Position = text.Position{LineNumber: lno}
return linematcher(m)
}
return -1
})
} else {
ectx = locateError(r, fe, linematcher)
}
if ectx.ChromaLexer == "" {
if fe.fileType != "" {
ectx.ChromaLexer = chromaLexerFromType(fe.fileType)
} else {
ectx.ChromaLexer = chromaLexerFromFilename(fe.Position().Filename)
}
}
fe.errorContext = ectx
if ectx.Position.LineNumber > 0 {
fe.position.LineNumber = ectx.Position.LineNumber
}
if ectx.Position.ColumnNumber > 0 {
fe.position.ColumnNumber = ectx.Position.ColumnNumber
}
return fe
}
type fileError struct {
position text.Position
position text.Position
errorContext *ErrorContext
fileType string
cause error
}
func (e *fileError) ErrorContext() *ErrorContext {
return e.errorContext
}
// Position returns the text position of this error.
func (e fileError) Position() text.Position {
return e.position
}
func (e *fileError) Type() string {
return e.fileType
func (e *fileError) Error() string {
return fmt.Sprintf("%s: %s", e.position, e.causeString())
}
func (e *fileError) Error() string {
func (e *fileError) causeString() string {
if e.cause == nil {
return ""
}
return e.cause.Error()
switch v := e.cause.(type) {
// Avoid repeating the file info in the error message.
case godartsass.SassError:
return v.Message
case libsasserrors.Error:
return v.Message
default:
return v.Error()
}
}
func (f *fileError) Cause() error {
return f.cause
func (e *fileError) Unwrap() error {
return e.cause
}
// NewFileError creates a new FileError.
func NewFileError(fileType string, offset, lineNumber, columnNumber int, err error) FileError {
pos := text.Position{Offset: offset, LineNumber: lineNumber, ColumnNumber: columnNumber}
// NewFileError creates a new FileError that wraps err.
// It will try to extract the filename and line number from err.
func NewFileError(err error) FileError {
// Filetype is used to determine the Chroma lexer to use.
fileType, pos := extractFileTypePos(err)
return &fileError{cause: err, fileType: fileType, position: pos}
}
// NewFileErrorFromName creates a new FileError that wraps err.
// The value for name should identify the file, the best
// being the full filename to the file on disk.
func NewFileErrorFromName(err error, name string) FileError {
// Filetype is used to determine the Chroma lexer to use.
fileType, pos := extractFileTypePos(err)
pos.Filename = name
if fileType == "" {
_, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(name))
}
return &fileError{cause: err, fileType: fileType, position: pos}
}
// NewFileErrorFromPos will use the filename and line number from pos to create a new FileError, wrapping err.
func NewFileErrorFromPos(err error, pos text.Position) FileError {
// Filetype is used to determine the Chroma lexer to use.
fileType, _ := extractFileTypePos(err)
if fileType == "" {
_, fileType = paths.FileAndExtNoDelimiter(filepath.Clean(pos.Filename))
}
return &fileError{cause: err, fileType: fileType, position: pos}
}
func NewFileErrorFromFileInErr(err error, fs afero.Fs, linematcher LineMatcherFn) FileError {
fe := NewFileError(err)
pos := fe.Position()
if pos.Filename == "" {
return fe
}
f, realFilename, err2 := openFile(pos.Filename, fs)
if err2 != nil {
return fe
}
pos.Filename = realFilename
defer f.Close()
return fe.UpdateContent(f, linematcher)
}
func NewFileErrorFromFileInPos(err error, pos text.Position, fs afero.Fs, linematcher LineMatcherFn) FileError {
if err == nil {
panic("err is nil")
}
f, realFilename, err2 := openFile(pos.Filename, fs)
if err2 != nil {
return NewFileErrorFromPos(err, pos)
}
pos.Filename = realFilename
defer f.Close()
return NewFileErrorFromPos(err, pos).UpdateContent(f, linematcher)
}
// NewFileErrorFromFile is a convenience method to create a new FileError from a file.
func NewFileErrorFromFile(err error, filename string, fs afero.Fs, linematcher LineMatcherFn) FileError {
if err == nil {
panic("err is nil")
}
f, realFilename, err2 := openFile(filename, fs)
if err2 != nil {
return NewFileErrorFromName(err, realFilename)
}
defer f.Close()
return NewFileErrorFromName(err, realFilename).UpdateContent(f, linematcher)
}
func openFile(filename string, fs afero.Fs) (afero.File, string, error) {
realFilename := filename
// We want the most specific filename possible in the error message.
fi, err2 := fs.Stat(filename)
if err2 == nil {
if s, ok := fi.(interface {
Filename() string
}); ok {
realFilename = s.Filename()
}
}
f, err2 := fs.Open(filename)
if err2 != nil {
return nil, realFilename, err2
}
return f, realFilename, nil
}
// Cause returns the underlying error or itself if it does not implement Unwrap.
func Cause(err error) error {
if u := errors.Unwrap(err); u != nil {
return u
}
return err
}
func extractFileTypePos(err error) (string, text.Position) {
err = Cause(err)
var fileType string
// LibSass, DartSass
if pos := extractPosition(err); pos.LineNumber > 0 || pos.Offset > 0 {
_, fileType = paths.FileAndExtNoDelimiter(pos.Filename)
return fileType, pos
}
// Default to line 1 col 1 if we don't find any better.
pos := text.Position{
Offset: -1,
LineNumber: 1,
ColumnNumber: 1,
}
// JSON errors.
offset, typ := extractOffsetAndType(err)
if fileType == "" {
fileType = typ
}
if offset >= 0 {
pos.Offset = offset
}
// The error type from the minifier contains line number and column number.
if line, col := exctractLineNumberAndColumnNumber(err); line >= 0 {
pos.LineNumber = line
pos.ColumnNumber = col
return fileType, pos
}
// Look in the error message for the line number.
for _, handle := range lineNumberExtractors {
lno, col := handle(err)
if lno > 0 {
pos.ColumnNumber = col
pos.LineNumber = lno
break
}
}
if fileType == "" && pos.Filename != "" {
_, fileType = paths.FileAndExtNoDelimiter(pos.Filename)
}
return fileType, pos
}
// UnwrapFileError tries to unwrap a FileError from err.
// It returns nil if this is not possible.
func UnwrapFileError(err error) FileError {
@@ -77,49 +322,38 @@ func UnwrapFileError(err error) FileError {
switch v := err.(type) {
case FileError:
return v
case causer:
err = v.Cause()
default:
return nil
err = errors.Unwrap(err)
}
}
return nil
}
// ToFileErrorWithOffset will return a new FileError with a line number
// with the given offset from the original.
func ToFileErrorWithOffset(fe FileError, offset int) FileError {
pos := fe.Position()
return ToFileErrorWithLineNumber(fe, pos.LineNumber+offset)
}
// ToFileErrorWithOffset will return a new FileError with the given line number.
func ToFileErrorWithLineNumber(fe FileError, lineNumber int) FileError {
pos := fe.Position()
pos.LineNumber = lineNumber
return &fileError{cause: fe, fileType: fe.Type(), position: pos}
}
// ToFileError will convert the given error to an error supporting
// the FileError interface.
func ToFileError(fileType string, err error) FileError {
for _, handle := range lineNumberExtractors {
lno, col := handle(err)
offset, typ := extractOffsetAndType(err)
if fileType == "" {
fileType = typ
}
if lno > 0 || offset != -1 {
return NewFileError(fileType, offset, lno, col, err)
// UnwrapFileErrors tries to unwrap all FileError.
func UnwrapFileErrors(err error) []FileError {
var errs []FileError
for err != nil {
if v, ok := err.(FileError); ok {
errs = append(errs, v)
}
err = errors.Unwrap(err)
}
// Fall back to the pointing to line number 1.
return NewFileError(fileType, -1, 1, 1, err)
return errs
}
// UnwrapFileErrorsWithErrorContext tries to unwrap all FileError in err that has an ErrorContext.
func UnwrapFileErrorsWithErrorContext(err error) []FileError {
var errs []FileError
for err != nil {
if v, ok := err.(FileError); ok && v.ErrorContext() != nil {
errs = append(errs, v)
}
err = errors.Unwrap(err)
}
return errs
}
func extractOffsetAndType(e error) (int, string) {
e = errors.Cause(e)
switch v := e.(type) {
case *json.UnmarshalTypeError:
return int(v.Offset), "json"
@@ -129,3 +363,32 @@ func extractOffsetAndType(e error) (int, string) {
return -1, ""
}
}
func exctractLineNumberAndColumnNumber(e error) (int, int) {
switch v := e.(type) {
case *parse.Error:
return v.Line, v.Column
case *toml.DecodeError:
return v.Position()
}
return -1, -1
}
func extractPosition(e error) (pos text.Position) {
switch v := e.(type) {
case godartsass.SassError:
span := v.Span
start := span.Start
filename, _ := paths.UrlToFilename(span.Url)
pos.Filename = filename
pos.Offset = start.Offset
pos.ColumnNumber = start.Column
case libsasserrors.Error:
pos.Filename = v.File
pos.LineNumber = v.Line
pos.ColumnNumber = v.Column
}
return
}
+37 -9
View File
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// 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.
@@ -14,14 +14,44 @@
package herrors
import (
"fmt"
"strings"
"testing"
"github.com/pkg/errors"
"errors"
"github.com/gohugoio/hugo/common/text"
qt "github.com/frankban/quicktest"
)
func TestToLineNumberError(t *testing.T) {
func TestNewFileError(t *testing.T) {
t.Parallel()
c := qt.New(t)
fe := NewFileErrorFromName(errors.New("bar"), "foo.html")
c.Assert(fe.Error(), qt.Equals, `"foo.html:1:1": bar`)
lines := ""
for i := 1; i <= 100; i++ {
lines += fmt.Sprintf("line %d\n", i)
}
fe.UpdatePosition(text.Position{LineNumber: 32, ColumnNumber: 2})
c.Assert(fe.Error(), qt.Equals, `"foo.html:32:2": bar`)
fe.UpdatePosition(text.Position{LineNumber: 0, ColumnNumber: 0, Offset: 212})
fe.UpdateContent(strings.NewReader(lines), nil)
c.Assert(fe.Error(), qt.Equals, `"foo.html:32:0": bar`)
errorContext := fe.ErrorContext()
c.Assert(errorContext, qt.IsNotNil)
c.Assert(errorContext.Lines, qt.DeepEquals, []string{"line 30", "line 31", "line 32", "line 33", "line 34"})
c.Assert(errorContext.LinesPos, qt.Equals, 2)
c.Assert(errorContext.ChromaLexer, qt.Equals, "go-html-template")
}
func TestNewFileErrorExtractFromMessage(t *testing.T) {
t.Parallel()
c := qt.New(t)
@@ -37,18 +67,16 @@ func TestToLineNumberError(t *testing.T) {
{errors.New("parse failed: template: _default/bundle-resource-meta.html:11: unexpected in operand"), 0, 11, 1},
{errors.New(`failed:: template: _default/bundle-resource-meta.html:2:7: executing "main" at <.Titles>`), 0, 2, 7},
{errors.New(`failed to load translations: (6, 7): was expecting token =, but got "g" instead`), 0, 6, 7},
{errors.New(`execute of template failed: template: index.html:2:5: executing "index.html" at <partial "foo.html" .>: error calling partial: "/layouts/partials/foo.html:3:6": execute of template failed: template: partials/foo.html:3:6: executing "partials/foo.html" at <.ThisDoesNotExist>: can't evaluate field ThisDoesNotExist in type *hugolib.pageStat`), 0, 2, 5},
} {
got := ToFileError("template", test.in)
got := NewFileErrorFromName(test.in, "test.txt")
errMsg := qt.Commentf("[%d][%T]", i, got)
le, ok := got.(FileError)
c.Assert(ok, qt.Equals, true)
c.Assert(ok, qt.Equals, true, errMsg)
pos := le.Position()
pos := got.Position()
c.Assert(pos.LineNumber, qt.Equals, test.lineNumber, errMsg)
c.Assert(pos.ColumnNumber, qt.Equals, test.columnNumber, errMsg)
c.Assert(errors.Cause(got), qt.Not(qt.IsNil))
c.Assert(errors.Unwrap(got), qt.Not(qt.IsNil))
}
}
+5 -19
View File
@@ -16,36 +16,22 @@ package herrors
import (
"regexp"
"strconv"
"github.com/pkg/errors"
"github.com/pelletier/go-toml/v2"
)
var lineNumberExtractors = []lineNumberExtractor{
// Template/shortcode parse errors
newLineNumberErrHandlerFromRegexp(".*:(\\d+):(\\d*):"),
newLineNumberErrHandlerFromRegexp(".*:(\\d+):"),
newLineNumberErrHandlerFromRegexp(`:(\d+):(\d*):`),
newLineNumberErrHandlerFromRegexp(`:(\d+):`),
// TOML parse errors
tomlLineNumberExtractor,
// YAML parse errors
newLineNumberErrHandlerFromRegexp("line (\\d+):"),
newLineNumberErrHandlerFromRegexp(`line (\d+):`),
// i18n bundle errors
newLineNumberErrHandlerFromRegexp("\\((\\d+),\\s(\\d*)"),
newLineNumberErrHandlerFromRegexp(`\((\d+),\s(\d*)`),
}
type lineNumberExtractor func(e error) (int, int)
var tomlLineNumberExtractor = func(e error) (int, int) {
e = errors.Cause(e)
if terr, ok := e.(*toml.DecodeError); ok {
return terr.Position()
}
return -1, -1
}
func newLineNumberErrHandlerFromRegexp(expression string) lineNumberExtractor {
re := regexp.MustCompile(expression)
return extractLineNo(re)
@@ -72,6 +58,6 @@ func extractLineNo(re *regexp.Regexp) lineNumberExtractor {
return lno, col
}
return -1, col
return 0, col
}
}
+4 -4
View File
@@ -128,7 +128,7 @@ type Exec struct {
// New will fail if name is not allowed according to the configured security policy.
// Else a configured Runner will be returned ready to be Run.
func (e *Exec) New(name string, arg ...interface{}) (Runner, error) {
func (e *Exec) New(name string, arg ...any) (Runner, error) {
if err := e.sc.CheckAllowedExec(name); err != nil {
return nil, err
}
@@ -146,8 +146,8 @@ func (e *Exec) New(name string, arg ...interface{}) (Runner, error) {
}
// Npx is a convenience method to create a Runner running npx --no-install <name> <args.
func (e *Exec) Npx(name string, arg ...interface{}) (Runner, error) {
arg = append(arg[:0], append([]interface{}{"--no-install", name}, arg[0:]...)...)
func (e *Exec) Npx(name string, arg ...any) (Runner, error) {
arg = append(arg[:0], append([]any{"--no-install", name}, arg[0:]...)...)
return e.New("npx", arg...)
}
@@ -205,7 +205,7 @@ type commandeer struct {
env []string
}
func (c *commandeer) command(arg ...interface{}) (*cmdWrapper, error) {
func (c *commandeer) command(arg ...any) (*cmdWrapper, error) {
if c == nil {
return nil, nil
}
+97 -1
View File
@@ -17,8 +17,12 @@
package hreflect
import (
"context"
"reflect"
"sync"
"time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/types"
)
@@ -60,7 +64,7 @@ func IsFloat(kind reflect.Kind) bool {
// IsTruthful returns whether in represents a truthful value.
// See IsTruthfulValue
func IsTruthful(in interface{}) bool {
func IsTruthful(in any) bool {
switch v := in.(type) {
case reflect.Value:
return IsTruthfulValue(v)
@@ -114,6 +118,96 @@ func IsTruthfulValue(val reflect.Value) (truth bool) {
return
}
type methodKey struct {
typ reflect.Type
name string
}
type methods struct {
sync.RWMutex
cache map[methodKey]int
}
var methodCache = &methods{cache: make(map[methodKey]int)}
// GetMethodByName is the same as reflect.Value.MethodByName, but it caches the
// type lookup.
func GetMethodByName(v reflect.Value, name string) reflect.Value {
index := GetMethodIndexByName(v.Type(), name)
if index == -1 {
return reflect.Value{}
}
return v.Method(index)
}
// GetMethodIndexByName returns the index of the method with the given name, or
// -1 if no such method exists.
func GetMethodIndexByName(tp reflect.Type, name string) int {
k := methodKey{tp, name}
methodCache.RLock()
index, found := methodCache.cache[k]
methodCache.RUnlock()
if found {
return index
}
methodCache.Lock()
defer methodCache.Unlock()
m, ok := tp.MethodByName(name)
index = m.Index
if !ok {
index = -1
}
methodCache.cache[k] = index
if !ok {
return -1
}
return m.Index
}
var (
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
asTimeProviderType = reflect.TypeOf((*htime.AsTimeProvider)(nil)).Elem()
)
// IsTime returns whether tp is a time.Time type or if it can be converted into one
// in ToTime.
func IsTime(tp reflect.Type) bool {
if tp == timeType {
return true
}
if tp.Implements(asTimeProviderType) {
return true
}
return false
}
// AsTime returns v as a time.Time if possible.
// The given location is only used if the value implements AsTimeProvider (e.g. go-toml local).
// A zero Time and false is returned if this isn't possible.
// Note that this function does not accept string dates.
func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) {
if v.Kind() == reflect.Interface {
return AsTime(v.Elem(), loc)
}
if v.Type() == timeType {
return v.Interface().(time.Time), true
}
if v.Type().Implements(asTimeProviderType) {
return v.Interface().(htime.AsTimeProvider).AsTime(loc), true
}
return time.Time{}, false
}
// Based on: https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L931
func indirectInterface(v reflect.Value) reflect.Value {
if v.Kind() != reflect.Interface {
@@ -124,3 +218,5 @@ func indirectInterface(v reflect.Value) reflect.Value {
}
return v.Elem()
}
var ContextInterface = reflect.TypeOf((*context.Context)(nil)).Elem()
+44
View File
@@ -30,6 +30,16 @@ func TestIsTruthful(t *testing.T) {
c.Assert(IsTruthful(time.Time{}), qt.Equals, false)
}
func TestGetMethodByName(t *testing.T) {
c := qt.New(t)
v := reflect.ValueOf(&testStruct{})
tp := v.Type()
c.Assert(GetMethodIndexByName(tp, "Method1"), qt.Equals, 0)
c.Assert(GetMethodIndexByName(tp, "Method3"), qt.Equals, 2)
c.Assert(GetMethodIndexByName(tp, "Foo"), qt.Equals, -1)
}
func BenchmarkIsTruthFul(b *testing.B) {
v := reflect.ValueOf("Hugo")
@@ -40,3 +50,37 @@ func BenchmarkIsTruthFul(b *testing.B) {
}
}
}
type testStruct struct{}
func (t *testStruct) Method1() string {
return "Hugo"
}
func (t *testStruct) Method2() string {
return "Hugo"
}
func (t *testStruct) Method3() string {
return "Hugo"
}
func (t *testStruct) Method4() string {
return "Hugo"
}
func (t *testStruct) Method5() string {
return "Hugo"
}
func BenchmarkGetMethodByName(b *testing.B) {
v := reflect.ValueOf(&testStruct{})
methods := []string{"Method1", "Method2", "Method3", "Method4", "Method5"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, method := range methods {
_ = GetMethodByName(v, method)
}
}
}
+21 -6
View File
@@ -17,10 +17,9 @@ import (
"strings"
"time"
"github.com/bep/clock"
"github.com/spf13/cast"
toml "github.com/pelletier/go-toml/v2"
"github.com/gohugoio/locales"
)
@@ -74,6 +73,8 @@ var (
"November",
"December",
}
Clock = clock.System()
)
func NewTimeFormatter(ltr locales.Translator) TimeFormatter {
@@ -134,17 +135,31 @@ func (f TimeFormatter) Format(t time.Time, layout string) string {
return s
}
func ToTimeInDefaultLocationE(i interface{}, location *time.Location) (tim time.Time, err error) {
func ToTimeInDefaultLocationE(i any, location *time.Location) (tim time.Time, err error) {
switch vv := i.(type) {
case toml.LocalDate:
return vv.AsTime(location), nil
case toml.LocalDateTime:
case AsTimeProvider:
return vv.AsTime(location), nil
// issue #8895
// datetimes parsed by `go-toml` have empty zone name
// convert back them into string and use `cast`
// TODO(bep) add tests, make sure we really need this.
case time.Time:
i = vv.Format(time.RFC3339)
}
return cast.ToTimeInDefaultLocationE(i, location)
}
// Now returns time.Now() or time value based on the `clock` flag.
// Use this function to fake time inside hugo.
func Now() time.Time {
return Clock.Now()
}
func Since(t time.Time) time.Duration {
return Clock.Since(t)
}
// AsTimeProvider is implemented by go-toml's LocalDate and LocalDateTime.
type AsTimeProvider interface {
AsTime(zone *time.Location) time.Time
}
+4 -7
View File
@@ -14,13 +14,10 @@
package hugio
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/spf13/afero"
)
@@ -54,13 +51,13 @@ func CopyFile(fs afero.Fs, from, to string) error {
// CopyDir copies a directory.
func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool) error {
fi, err := os.Stat(from)
fi, err := fs.Stat(from)
if err != nil {
return err
}
if !fi.IsDir() {
return errors.Errorf("%q is not a directory", from)
return fmt.Errorf("%q is not a directory", from)
}
err = fs.MkdirAll(to, 0777) // before umask
@@ -68,7 +65,7 @@ func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool
return err
}
entries, _ := ioutil.ReadDir(from)
entries, _ := afero.ReadDir(fs, from)
for _, entry := range entries {
fromFilename := filepath.Join(from, entry.Name())
toFilename := filepath.Join(to, entry.Name())
+57
View File
@@ -0,0 +1,57 @@
// 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 hugio
import (
"bytes"
)
// HasBytesWriter is a writer that will set Match to true if the given pattern
// is found in the stream.
type HasBytesWriter struct {
Match bool
Pattern []byte
i int
done bool
buff []byte
}
func (h *HasBytesWriter) Write(p []byte) (n int, err error) {
if h.done {
return len(p), nil
}
if len(h.buff) == 0 {
h.buff = make([]byte, len(h.Pattern)*2)
}
for i := range p {
h.buff[h.i] = p[i]
h.i++
if h.i == len(h.buff) {
// Shift left.
copy(h.buff, h.buff[len(h.buff)/2:])
h.i = len(h.buff) / 2
}
if bytes.Contains(h.buff, h.Pattern) {
h.Match = true
h.done = true
return len(p), nil
}
}
return len(p), nil
}
+64
View File
@@ -0,0 +1,64 @@
// 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 hugio
import (
"bytes"
"fmt"
"io"
"math/rand"
"strings"
"testing"
"time"
qt "github.com/frankban/quicktest"
)
func TestHasBytesWriter(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
c := qt.New((t))
neww := func() (*HasBytesWriter, io.Writer) {
var b bytes.Buffer
h := &HasBytesWriter{
Pattern: []byte("__foo"),
}
return h, io.MultiWriter(&b, h)
}
rndStr := func() string {
return strings.Repeat("ab cfo", r.Intn(33))
}
for i := 0; i < 22; i++ {
h, w := neww()
fmt.Fprintf(w, rndStr()+"abc __foobar"+rndStr())
c.Assert(h.Match, qt.Equals, true)
h, w = neww()
fmt.Fprintf(w, rndStr()+"abc __f")
fmt.Fprintf(w, "oo bar"+rndStr())
c.Assert(h.Match, qt.Equals, true)
h, w = neww()
fmt.Fprintf(w, rndStr()+"abc __moo bar")
c.Assert(h.Match, qt.Equals, false)
}
h, w := neww()
fmt.Fprintf(w, "__foo")
c.Assert(h.Match, qt.Equals, true)
}
+8
View File
@@ -18,6 +18,14 @@ import (
"io/ioutil"
)
// As implemented by strings.Builder.
type FlexiWriter interface {
io.Writer
io.ByteWriter
WriteString(s string) (int, error)
WriteRune(r rune) (int, error)
}
type multiWriteCloser struct {
io.Writer
closers []io.WriteCloser
+67 -8
View File
@@ -21,6 +21,7 @@ import (
"runtime/debug"
"sort"
"strings"
"sync"
"time"
"github.com/gohugoio/hugo/hugofs/files"
@@ -37,13 +38,8 @@ const (
)
var (
// commitHash contains the current Git revision.
// Use mage to build to make sure this gets set.
commitHash string
// buildDate contains the date of the current build.
// buildDate allows vendor-specified build date when .git/ is unavailable.
buildDate string
// vendorInfo contains vendor notes about the current build.
vendorInfo string
)
@@ -59,6 +55,9 @@ type Info struct {
// It can be any string, but it will be all lower case.
Environment string
// version of go that the Hugo binary was built with
GoVersion string
deps []*Dependency
}
@@ -90,11 +89,25 @@ func NewInfo(environment string, deps []*Dependency) Info {
if environment == "" {
environment = EnvironmentProduction
}
var (
commitHash string
buildDate string
goVersion string
)
bi := getBuildInfo()
if bi != nil {
commitHash = bi.Revision
buildDate = bi.RevisionTime
goVersion = bi.GoVersion
}
return Info{
CommitHash: commitHash,
BuildDate: buildDate,
Environment: environment,
deps: deps,
GoVersion: goVersion,
}
}
@@ -125,6 +138,52 @@ func GetExecEnviron(workDir string, cfg config.Provider, fs afero.Fs) []string {
return env
}
type buildInfo struct {
VersionControlSystem string
Revision string
RevisionTime string
Modified bool
GoOS string
GoArch string
*debug.BuildInfo
}
var bInfo *buildInfo
var bInfoInit sync.Once
func getBuildInfo() *buildInfo {
bInfoInit.Do(func() {
bi, ok := debug.ReadBuildInfo()
if !ok {
return
}
bInfo = &buildInfo{BuildInfo: bi}
for _, s := range bInfo.Settings {
switch s.Key {
case "vcs":
bInfo.VersionControlSystem = s.Value
case "vcs.revision":
bInfo.Revision = s.Value
case "vcs.time":
bInfo.RevisionTime = s.Value
case "vcs.modified":
bInfo.Modified = s.Value == "true"
case "GOOS":
bInfo.GoOS = s.Value
case "GOARCH":
bInfo.GoArch = s.Value
}
}
})
return bInfo
}
// GetDependencyList returns a sorted dependency list on the format package="version".
// It includes both Go dependencies and (a manually maintained) list of C(++) dependencies.
func GetDependencyList() []string {
@@ -143,8 +202,8 @@ func GetDependencyList() []string {
)
}
bi, ok := debug.ReadBuildInfo()
if !ok {
bi := getBuildInfo()
if bi == nil {
return deps
}
+7 -2
View File
@@ -27,8 +27,13 @@ func TestHugoInfo(t *testing.T) {
c.Assert(hugoInfo.Version(), qt.Equals, CurrentVersion.Version())
c.Assert(fmt.Sprintf("%T", VersionString("")), qt.Equals, fmt.Sprintf("%T", hugoInfo.Version()))
c.Assert(hugoInfo.CommitHash, qt.Equals, commitHash)
c.Assert(hugoInfo.BuildDate, qt.Equals, buildDate)
bi := getBuildInfo()
if bi != nil {
c.Assert(hugoInfo.CommitHash, qt.Equals, bi.Revision)
c.Assert(hugoInfo.BuildDate, qt.Equals, bi.RevisionTime)
c.Assert(hugoInfo.GoVersion, qt.Equals, bi.GoVersion)
}
c.Assert(hugoInfo.Environment, qt.Equals, "production")
c.Assert(string(hugoInfo.Generator()), qt.Contains, fmt.Sprintf("Hugo %s", hugoInfo.Version()))
c.Assert(hugoInfo.IsProduction(), qt.Equals, true)
+1
View File
@@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build extended
// +build extended
package hugo
+1
View File
@@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !extended
// +build !extended
package hugo
+99 -63
View File
@@ -16,7 +16,9 @@ package hugo
import (
"fmt"
"io"
"math"
"runtime"
"strconv"
"strings"
"github.com/gohugoio/hugo/compare"
@@ -25,8 +27,9 @@ import (
// Version represents the Hugo build version.
type Version struct {
// Major and minor version.
Number float32
Major int
Minor int
// Increment this for bug releases
PatchLevel int
@@ -42,7 +45,7 @@ var (
)
func (v Version) String() string {
return version(v.Number, v.PatchLevel, v.Suffix)
return version(v.Major, v.Minor, v.PatchLevel, v.Suffix)
}
// Version returns the Hugo version.
@@ -50,6 +53,11 @@ func (v Version) Version() VersionString {
return VersionString(v.String())
}
// Compare implements the compare.Comparer interface.
func (h Version) Compare(other any) int {
return compareVersions(h, other)
}
// VersionString represents a Hugo version string.
type VersionString string
@@ -58,13 +66,13 @@ func (h VersionString) String() string {
}
// Compare implements the compare.Comparer interface.
func (h VersionString) Compare(other interface{}) int {
func (h VersionString) Compare(other any) int {
v := MustParseVersion(h.String())
return compareVersionsWithSuffix(v.Number, v.PatchLevel, v.Suffix, other)
return compareVersions(v, other)
}
// Eq implements the compare.Eqer interface.
func (h VersionString) Eq(other interface{}) bool {
func (h VersionString) Eq(other any) bool {
s, err := cast.ToStringE(other)
if err != nil {
return false
@@ -84,10 +92,7 @@ func ParseVersion(s string) (Version, error) {
}
}
v, p := parseVersion(s)
vv.Number = v
vv.PatchLevel = p
vv.Major, vv.Minor, vv.PatchLevel = parseVersion(s)
return vv, nil
}
@@ -110,18 +115,20 @@ func (v Version) ReleaseVersion() Version {
// Next returns the next Hugo release version.
func (v Version) Next() Version {
return Version{Number: v.Number + 0.01}
return Version{Major: v.Major, Minor: v.Minor + 1}
}
// Prev returns the previous Hugo release version.
func (v Version) Prev() Version {
return Version{Number: v.Number - 0.01}
return Version{Major: v.Major, Minor: v.Minor - 1}
}
// NextPatchLevel returns the next patch/bugfix Hugo version.
// This will be a patch increment on the previous Hugo version.
func (v Version) NextPatchLevel(level int) Version {
return Version{Number: v.Number - 0.01, PatchLevel: level}
prev := v.Prev()
prev.PatchLevel = level
return prev
}
// BuildVersionString creates a version string. This is what you see when
@@ -131,16 +138,25 @@ func BuildVersionString() string {
program := "hugo"
version := "v" + CurrentVersion.String()
if commitHash != "" {
version += "-" + strings.ToUpper(commitHash)
bi := getBuildInfo()
if bi == nil {
return version
}
if bi.Revision != "" {
version += "-" + bi.Revision
}
if IsExtended {
version += "+extended"
}
osArch := runtime.GOOS + "/" + runtime.GOARCH
osArch := bi.GoOS + "/" + bi.GoArch
date := buildDate
date := bi.RevisionTime
if date == "" {
// Accept vendor-specified build date if .git/ is unavailable.
date = buildDate
}
if date == "" {
date = "unknown"
}
@@ -155,38 +171,53 @@ func BuildVersionString() string {
return versionString
}
func version(version float32, patchVersion int, suffix string) string {
if patchVersion > 0 || version > 0.53 {
return fmt.Sprintf("%.2f.%d%s", version, patchVersion, suffix)
func version(major, minor, patch int, suffix string) string {
if patch > 0 || minor > 53 {
return fmt.Sprintf("%d.%d.%d%s", major, minor, patch, suffix)
}
return fmt.Sprintf("%.2f%s", version, suffix)
return fmt.Sprintf("%d.%d%s", major, minor, suffix)
}
// CompareVersion compares the given version string or number against the
// running Hugo version.
// It returns -1 if the given version is less than, 0 if equal and 1 if greater than
// the running version.
func CompareVersion(version interface{}) int {
return compareVersionsWithSuffix(CurrentVersion.Number, CurrentVersion.PatchLevel, CurrentVersion.Suffix, version)
func CompareVersion(version any) int {
return compareVersions(CurrentVersion, version)
}
func compareVersions(inVersion float32, inPatchVersion int, in interface{}) int {
return compareVersionsWithSuffix(inVersion, inPatchVersion, "", in)
}
func compareVersionsWithSuffix(inVersion float32, inPatchVersion int, suffix string, in interface{}) int {
func compareVersions(inVersion Version, in any) int {
var c int
switch d := in.(type) {
case float64:
c = compareFloatVersions(inVersion, float32(d))
c = compareFloatWithVersion(d, inVersion)
case float32:
c = compareFloatVersions(inVersion, d)
c = compareFloatWithVersion(float64(d), inVersion)
case int:
c = compareFloatVersions(inVersion, float32(d))
c = compareFloatWithVersion(float64(d), inVersion)
case int32:
c = compareFloatVersions(inVersion, float32(d))
c = compareFloatWithVersion(float64(d), inVersion)
case int64:
c = compareFloatVersions(inVersion, float32(d))
c = compareFloatWithVersion(float64(d), inVersion)
case Version:
if d.Major == inVersion.Major && d.Minor == inVersion.Minor && d.PatchLevel == inVersion.PatchLevel {
return strings.Compare(inVersion.Suffix, d.Suffix)
}
if d.Major > inVersion.Major {
return 1
} else if d.Major < inVersion.Major {
return -1
}
if d.Minor > inVersion.Minor {
return 1
} else if d.Minor < inVersion.Minor {
return -1
}
if d.PatchLevel > inVersion.PatchLevel {
return 1
} else if d.PatchLevel < inVersion.PatchLevel {
return -1
}
default:
s, err := cast.ToStringE(in)
if err != nil {
@@ -197,50 +228,55 @@ func compareVersionsWithSuffix(inVersion float32, inPatchVersion int, suffix str
if err != nil {
return -1
}
return inVersion.Compare(v)
if v.Number == inVersion && v.PatchLevel == inPatchVersion {
return strings.Compare(suffix, v.Suffix)
}
if v.Number < inVersion || (v.Number == inVersion && v.PatchLevel < inPatchVersion) {
return -1
}
return 1
}
if c == 0 && suffix != "" {
return 1
}
return c
}
func parseVersion(s string) (float32, int) {
var (
v float32
p int
)
if strings.Count(s, ".") == 2 {
li := strings.LastIndex(s, ".")
p = cast.ToInt(s[li+1:])
s = s[:li]
func parseVersion(s string) (int, int, int) {
var major, minor, patch int
parts := strings.Split(s, ".")
if len(parts) > 0 {
major, _ = strconv.Atoi(parts[0])
}
if len(parts) > 1 {
minor, _ = strconv.Atoi(parts[1])
}
if len(parts) > 2 {
patch, _ = strconv.Atoi(parts[2])
}
v = float32(cast.ToFloat64(s))
return v, p
return major, minor, patch
}
func compareFloatVersions(version float32, v float32) int {
if v == version {
// compareFloatWithVersion compares v1 with v2.
// It returns -1 if v1 is less than v2, 0 if v1 is equal to v2 and 1 if v1 is greater than v2.
func compareFloatWithVersion(v1 float64, v2 Version) int {
mf, minf := math.Modf(v1)
v1maj := int(mf)
v1min := int(minf * 100)
if v2.Major == v1maj && v2.Minor == v1min {
return 0
}
if v < version {
if v1maj > v2.Major {
return 1
}
if v1maj < v2.Major {
return -1
}
return 1
if v1min > v2.Minor {
return 1
}
return -1
}
func GoMinorVersion() int {
+4 -3
View File
@@ -16,7 +16,8 @@ package hugo
// CurrentVersion represents the current build version.
// This should be the only one.
var CurrentVersion = Version{
Number: 0.92,
PatchLevel: 2,
Suffix: "",
Major: 0,
Minor: 105,
PatchLevel: 0,
Suffix: "-DEV",
}
+24 -25
View File
@@ -22,10 +22,10 @@ import (
func TestHugoVersion(t *testing.T) {
c := qt.New(t)
c.Assert(version(0.15, 0, "-DEV"), qt.Equals, "0.15-DEV")
c.Assert(version(0.15, 2, "-DEV"), qt.Equals, "0.15.2-DEV")
c.Assert(version(0, 15, 0, "-DEV"), qt.Equals, "0.15-DEV")
c.Assert(version(0, 15, 2, "-DEV"), qt.Equals, "0.15.2-DEV")
v := Version{Number: 0.21, PatchLevel: 0, Suffix: "-DEV"}
v := Version{Minor: 21, Suffix: "-DEV"}
c.Assert(v.ReleaseVersion().String(), qt.Equals, "0.21")
c.Assert(v.String(), qt.Equals, "0.21-DEV")
@@ -39,37 +39,36 @@ func TestHugoVersion(t *testing.T) {
// We started to use full semver versions even for main
// releases in v0.54.0
v = Version{Number: 0.53, PatchLevel: 0}
v = Version{Minor: 53, PatchLevel: 0}
c.Assert(v.String(), qt.Equals, "0.53")
c.Assert(v.Next().String(), qt.Equals, "0.54.0")
c.Assert(v.Next().Next().String(), qt.Equals, "0.55.0")
v = Version{Number: 0.54, PatchLevel: 0, Suffix: "-DEV"}
v = Version{Minor: 54, PatchLevel: 0, Suffix: "-DEV"}
c.Assert(v.String(), qt.Equals, "0.54.0-DEV")
}
func TestCompareVersions(t *testing.T) {
c := qt.New(t)
c.Assert(compareVersions(0.20, 0, 0.20), qt.Equals, 0)
c.Assert(compareVersions(0.20, 0, float32(0.20)), qt.Equals, 0)
c.Assert(compareVersions(0.20, 0, float64(0.20)), qt.Equals, 0)
c.Assert(compareVersions(0.19, 1, 0.20), qt.Equals, 1)
c.Assert(compareVersions(0.19, 3, "0.20.2"), qt.Equals, 1)
c.Assert(compareVersions(0.19, 1, 0.01), qt.Equals, -1)
c.Assert(compareVersions(0, 1, 3), qt.Equals, 1)
c.Assert(compareVersions(0, 1, int32(3)), qt.Equals, 1)
c.Assert(compareVersions(0, 1, int64(3)), qt.Equals, 1)
c.Assert(compareVersions(0.20, 0, "0.20"), qt.Equals, 0)
c.Assert(compareVersions(0.20, 1, "0.20.1"), qt.Equals, 0)
c.Assert(compareVersions(0.20, 1, "0.20"), qt.Equals, -1)
c.Assert(compareVersions(0.20, 0, "0.20.1"), qt.Equals, 1)
c.Assert(compareVersions(0.20, 1, "0.20.2"), qt.Equals, 1)
c.Assert(compareVersions(0.21, 1, "0.22.1"), qt.Equals, 1)
c.Assert(compareVersions(0.22, 0, "0.22-DEV"), qt.Equals, -1)
c.Assert(compareVersions(0.22, 0, "0.22.1-DEV"), qt.Equals, 1)
c.Assert(compareVersionsWithSuffix(0.22, 0, "-DEV", "0.22"), qt.Equals, 1)
c.Assert(compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22"), qt.Equals, -1)
c.Assert(compareVersionsWithSuffix(0.22, 1, "-DEV", "0.22.1-DEV"), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.0"), 0.20), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.0"), float32(0.20)), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.0"), float64(0.20)), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.19.1"), 0.20), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.19.3"), "0.20.2"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.1"), 3), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.1"), int32(3)), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.1"), int64(3)), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.20"), "0.20"), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.1"), "0.20.1"), qt.Equals, 0)
c.Assert(compareVersions(MustParseVersion("0.20.1"), "0.20"), qt.Equals, -1)
c.Assert(compareVersions(MustParseVersion("0.20.0"), "0.20.1"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.20.1"), "0.20.2"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.21.1"), "0.22.1"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.22.0"), "0.22-DEV"), qt.Equals, -1)
c.Assert(compareVersions(MustParseVersion("0.22.0"), "0.22.1-DEV"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.22.0-DEV"), "0.22"), qt.Equals, 1)
c.Assert(compareVersions(MustParseVersion("0.22.1-DEV"), "0.22"), qt.Equals, -1)
c.Assert(compareVersions(MustParseVersion("0.22.1-DEV"), "0.22.1-DEV"), qt.Equals, 0)
}
func TestParseHugoVersion(t *testing.T) {
+2 -2
View File
@@ -21,7 +21,7 @@ import (
// IgnorableLogger is a logger that ignores certain log statements.
type IgnorableLogger interface {
Logger
Errorsf(statementID, format string, v ...interface{})
Errorsf(statementID, format string, v ...any)
Apply(logger Logger) IgnorableLogger
}
@@ -43,7 +43,7 @@ func NewIgnorableLogger(logger Logger, statements ...string) IgnorableLogger {
}
// Errorsf logs statementID as an ERROR if not configured as ignoreable.
func (l ignorableLogger) Errorsf(statementID, format string, v ...interface{}) {
func (l ignorableLogger) Errorsf(statementID, format string, v ...any) {
if l.statements[statementID] {
// Ignore.
return
+26 -21
View File
@@ -58,21 +58,21 @@ func (w prefixWriter) Write(p []byte) (n int, err error) {
}
type Logger interface {
Printf(format string, v ...interface{})
Println(v ...interface{})
Printf(format string, v ...any)
Println(v ...any)
PrintTimerIfDelayed(start time.Time, name string)
Debug() *log.Logger
Debugf(format string, v ...interface{})
Debugln(v ...interface{})
Debugf(format string, v ...any)
Debugln(v ...any)
Info() *log.Logger
Infof(format string, v ...interface{})
Infoln(v ...interface{})
Infof(format string, v ...any)
Infoln(v ...any)
Warn() *log.Logger
Warnf(format string, v ...interface{})
Warnln(v ...interface{})
Warnf(format string, v ...any)
Warnln(v ...any)
Error() *log.Logger
Errorf(format string, v ...interface{})
Errorln(v ...interface{})
Errorf(format string, v ...any)
Errorln(v ...any)
Errors() string
Out() io.Writer
@@ -101,11 +101,11 @@ type logger struct {
errors *bytes.Buffer
}
func (l *logger) Printf(format string, v ...interface{}) {
func (l *logger) Printf(format string, v ...any) {
l.FEEDBACK.Printf(format, v...)
}
func (l *logger) Println(v ...interface{}) {
func (l *logger) Println(v ...any) {
l.FEEDBACK.Println(v...)
}
@@ -113,19 +113,19 @@ func (l *logger) Debug() *log.Logger {
return l.DEBUG
}
func (l *logger) Debugf(format string, v ...interface{}) {
func (l *logger) Debugf(format string, v ...any) {
l.DEBUG.Printf(format, v...)
}
func (l *logger) Debugln(v ...interface{}) {
func (l *logger) Debugln(v ...any) {
l.DEBUG.Println(v...)
}
func (l *logger) Infof(format string, v ...interface{}) {
func (l *logger) Infof(format string, v ...any) {
l.INFO.Printf(format, v...)
}
func (l *logger) Infoln(v ...interface{}) {
func (l *logger) Infoln(v ...any) {
l.INFO.Println(v...)
}
@@ -135,14 +135,14 @@ func (l *logger) Info() *log.Logger {
const panicOnWarningMessage = "Warning trapped. Remove the --panicOnWarning flag to continue."
func (l *logger) Warnf(format string, v ...interface{}) {
func (l *logger) Warnf(format string, v ...any) {
l.WARN.Printf(format, v...)
if PanicOnWarning {
panic(panicOnWarningMessage)
}
}
func (l *logger) Warnln(v ...interface{}) {
func (l *logger) Warnln(v ...any) {
l.WARN.Println(v...)
if PanicOnWarning {
panic(panicOnWarningMessage)
@@ -153,11 +153,11 @@ func (l *logger) Warn() *log.Logger {
return l.WARN
}
func (l *logger) Errorf(format string, v ...interface{}) {
func (l *logger) Errorf(format string, v ...any) {
l.ERROR.Printf(format, v...)
}
func (l *logger) Errorln(v ...interface{}) {
func (l *logger) Errorln(v ...any) {
l.ERROR.Println(v...)
}
@@ -240,6 +240,11 @@ func NewBasicLoggerForWriter(t jww.Threshold, w io.Writer) Logger {
return newLogger(t, jww.LevelError, w, ioutil.Discard, false)
}
// RemoveANSIColours removes all ANSI colours from the given string.
func RemoveANSIColours(s string) string {
return ansiColorRe.ReplaceAllString(s, "")
}
var (
ansiColorRe = regexp.MustCompile("(?s)\\033\\[\\d*(;\\d*)*m")
errorRe = regexp.MustCompile("^(ERROR|FATAL|WARN)")
@@ -285,7 +290,7 @@ func InitGlobalLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, lo
}
func getLogWriters(outHandle, logHandle io.Writer) (io.Writer, io.Writer) {
isTerm := terminal.IsTerminal(os.Stdout)
isTerm := terminal.PrintANSIColors(os.Stdout)
if logHandle != ioutil.Discard && isTerm {
// Remove any Ansi coloring from log output
logHandle = ansiCleaner{w: logHandle}
+12
View File
@@ -46,3 +46,15 @@ func TestLoggerToWriterWithPrefix(t *testing.T) {
c.Assert(b.String(), qt.Equals, "myprefix: Hello Hugo!\n")
}
func TestRemoveANSIColours(t *testing.T) {
c := qt.New(t)
c.Assert(RemoveANSIColours(""), qt.Equals, "")
c.Assert(RemoveANSIColours("\033[31m"), qt.Equals, "")
c.Assert(RemoveANSIColours("\033[31mHello"), qt.Equals, "Hello")
c.Assert(RemoveANSIColours("\033[31mHello\033[0m"), qt.Equals, "Hello")
c.Assert(RemoveANSIColours("\033[31mHello\033[0m World"), qt.Equals, "Hello World")
c.Assert(RemoveANSIColours("\033[31mHello\033[0m World\033[31m!"), qt.Equals, "Hello World!")
c.Assert(RemoveANSIColours("\x1b[90m 5 |"), qt.Equals, " 5 |")
}
+33 -19
View File
@@ -24,12 +24,12 @@ import (
)
// ToStringMapE converts in to map[string]interface{}.
func ToStringMapE(in interface{}) (map[string]interface{}, error) {
func ToStringMapE(in any) (map[string]any, error) {
switch vv := in.(type) {
case Params:
return vv, nil
case map[string]string:
var m = map[string]interface{}{}
var m = map[string]any{}
for k, v := range vv {
m[k] = v
}
@@ -43,7 +43,7 @@ func ToStringMapE(in interface{}) (map[string]interface{}, error) {
// ToParamsAndPrepare converts in to Params and prepares it for use.
// If in is nil, an empty map is returned.
// See PrepareParams.
func ToParamsAndPrepare(in interface{}) (Params, bool) {
func ToParamsAndPrepare(in any) (Params, bool) {
if types.IsNil(in) {
return Params{}, true
}
@@ -56,7 +56,7 @@ func ToParamsAndPrepare(in interface{}) (Params, bool) {
}
// MustToParamsAndPrepare calls ToParamsAndPrepare and panics if it fails.
func MustToParamsAndPrepare(in interface{}) Params {
func MustToParamsAndPrepare(in any) Params {
if p, ok := ToParamsAndPrepare(in); ok {
return p
} else {
@@ -65,13 +65,13 @@ func MustToParamsAndPrepare(in interface{}) Params {
}
// ToStringMap converts in to map[string]interface{}.
func ToStringMap(in interface{}) map[string]interface{} {
func ToStringMap(in any) map[string]any {
m, _ := ToStringMapE(in)
return m
}
// ToStringMapStringE converts in to map[string]string.
func ToStringMapStringE(in interface{}) (map[string]string, error) {
func ToStringMapStringE(in any) (map[string]string, error) {
m, err := ToStringMapE(in)
if err != nil {
return nil, err
@@ -80,26 +80,26 @@ func ToStringMapStringE(in interface{}) (map[string]string, error) {
}
// ToStringMapString converts in to map[string]string.
func ToStringMapString(in interface{}) map[string]string {
func ToStringMapString(in any) map[string]string {
m, _ := ToStringMapStringE(in)
return m
}
// ToStringMapBool converts in to bool.
func ToStringMapBool(in interface{}) map[string]bool {
func ToStringMapBool(in any) map[string]bool {
m, _ := ToStringMapE(in)
return cast.ToStringMapBool(m)
}
// ToSliceStringMap converts in to []map[string]interface{}.
func ToSliceStringMap(in interface{}) ([]map[string]interface{}, error) {
func ToSliceStringMap(in any) ([]map[string]any, error) {
switch v := in.(type) {
case []map[string]interface{}:
case []map[string]any:
return v, nil
case []interface{}:
var s []map[string]interface{}
case []any:
var s []map[string]any
for _, entry := range v {
if vv, ok := entry.(map[string]interface{}); ok {
if vv, ok := entry.(map[string]any); ok {
s = append(s, vv)
}
}
@@ -109,6 +109,20 @@ func ToSliceStringMap(in interface{}) ([]map[string]interface{}, error) {
}
}
// LookupEqualFold finds key in m with case insensitive equality checks.
func LookupEqualFold[T any | string](m map[string]T, key string) (T, bool) {
if v, found := m[key]; found {
return v, true
}
for k, v := range m {
if strings.EqualFold(k, key) {
return v, true
}
}
var s T
return s, false
}
type keyRename struct {
pattern glob.Glob
newKey string
@@ -146,7 +160,7 @@ func (r KeyRenamer) getNewKey(keyPath string) string {
// Rename renames the keys in the given map according
// to the patterns in the current KeyRenamer.
func (r KeyRenamer) Rename(m map[string]interface{}) {
func (r KeyRenamer) Rename(m map[string]any) {
r.renamePath("", m)
}
@@ -158,15 +172,15 @@ func (KeyRenamer) keyPath(k1, k2 string) string {
return k1 + "/" + k2
}
func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]interface{}) {
func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]any) {
for key, val := range m {
keyPath := r.keyPath(parentKeyPath, key)
switch val.(type) {
case map[interface{}]interface{}:
case map[any]any:
val = cast.ToStringMap(val)
r.renamePath(keyPath, val.(map[string]interface{}))
case map[string]interface{}:
r.renamePath(keyPath, val.(map[string]interface{}))
r.renamePath(keyPath, val.(map[string]any))
case map[string]any:
r.renamePath(keyPath, val.(map[string]any))
}
newKey := r.getNewKey(keyPath)
+44 -21
View File
@@ -27,7 +27,7 @@ func TestPrepareParams(t *testing.T) {
expected Params
}{
{
map[string]interface{}{
map[string]any{
"abC": 32,
},
Params{
@@ -35,16 +35,16 @@ func TestPrepareParams(t *testing.T) {
},
},
{
map[string]interface{}{
map[string]any{
"abC": 32,
"deF": map[interface{}]interface{}{
"deF": map[any]any{
23: "A value",
24: map[string]interface{}{
24: map[string]any{
"AbCDe": "A value",
"eFgHi": "Another value",
},
},
"gHi": map[string]interface{}{
"gHi": map[string]any{
"J": 25,
},
"jKl": map[string]string{
@@ -85,23 +85,23 @@ func TestToSliceStringMap(t *testing.T) {
c := qt.New(t)
tests := []struct {
input interface{}
expected []map[string]interface{}
input any
expected []map[string]any
}{
{
input: []map[string]interface{}{
input: []map[string]any{
{"abc": 123},
},
expected: []map[string]interface{}{
expected: []map[string]any{
{"abc": 123},
},
}, {
input: []interface{}{
map[string]interface{}{
input: []any{
map[string]any{
"def": 456,
},
},
expected: []map[string]interface{}{
expected: []map[string]any{
{"def": 456},
},
},
@@ -116,7 +116,7 @@ func TestToSliceStringMap(t *testing.T) {
func TestToParamsAndPrepare(t *testing.T) {
c := qt.New(t)
_, ok := ToParamsAndPrepare(map[string]interface{}{"A": "av"})
_, ok := ToParamsAndPrepare(map[string]any{"A": "av"})
c.Assert(ok, qt.IsTrue)
params, ok := ToParamsAndPrepare(nil)
@@ -127,33 +127,33 @@ func TestToParamsAndPrepare(t *testing.T) {
func TestRenameKeys(t *testing.T) {
c := qt.New(t)
m := map[string]interface{}{
m := map[string]any{
"a": 32,
"ren1": "m1",
"ren2": "m1_2",
"sub": map[string]interface{}{
"subsub": map[string]interface{}{
"sub": map[string]any{
"subsub": map[string]any{
"REN1": "m2",
"ren2": "m2_2",
},
},
"no": map[string]interface{}{
"no": map[string]any{
"ren1": "m2",
"ren2": "m2_2",
},
}
expected := map[string]interface{}{
expected := map[string]any{
"a": 32,
"new1": "m1",
"new2": "m1_2",
"sub": map[string]interface{}{
"subsub": map[string]interface{}{
"sub": map[string]any{
"subsub": map[string]any{
"new1": "m2",
"ren2": "m2_2",
},
},
"no": map[string]interface{}{
"no": map[string]any{
"ren1": "m2",
"ren2": "m2_2",
},
@@ -171,3 +171,26 @@ func TestRenameKeys(t *testing.T) {
t.Errorf("Expected\n%#v, got\n%#v\n", expected, m)
}
}
func TestLookupEqualFold(t *testing.T) {
c := qt.New(t)
m1 := map[string]any{
"a": "av",
"B": "bv",
}
v, found := LookupEqualFold(m1, "b")
c.Assert(found, qt.IsTrue)
c.Assert(v, qt.Equals, "bv")
m2 := map[string]string{
"a": "av",
"B": "bv",
}
v, found = LookupEqualFold(m2, "b")
c.Assert(found, qt.IsTrue)
c.Assert(v, qt.Equals, "bv")
}
+11 -11
View File
@@ -21,11 +21,11 @@ import (
)
// Params is a map where all keys are lower case.
type Params map[string]interface{}
type Params map[string]any
// Get does a lower case and nested search in this map.
// It will return nil if none found.
func (p Params) Get(indices ...string) interface{} {
func (p Params) Get(indices ...string) any {
v, _, _ := getNested(p, indices)
return v
}
@@ -142,7 +142,7 @@ func (p Params) SetDefaultMergeStrategy(s ParamsMergeStrategy) {
p[mergeStrategyKey] = s
}
func getNested(m map[string]interface{}, indices []string) (interface{}, string, map[string]interface{}) {
func getNested(m map[string]any, indices []string) (any, string, map[string]any) {
if len(indices) == 0 {
return nil, "", nil
}
@@ -164,7 +164,7 @@ func getNested(m map[string]interface{}, indices []string) (interface{}, string,
switch m2 := v.(type) {
case Params:
return getNested(m2, indices[1:])
case map[string]interface{}:
case map[string]any:
return getNested(m2, indices[1:])
default:
return nil, "", nil
@@ -175,7 +175,7 @@ func getNested(m map[string]interface{}, indices []string) (interface{}, string,
// It will first try the exact match and then try to find it as a nested map value,
// using the given separator, e.g. "mymap.name".
// It assumes that all the maps given have lower cased keys.
func GetNestedParam(keyStr, separator string, candidates ...Params) (interface{}, error) {
func GetNestedParam(keyStr, separator string, candidates ...Params) (any, error) {
keyStr = strings.ToLower(keyStr)
// Try exact match first
@@ -195,7 +195,7 @@ func GetNestedParam(keyStr, separator string, candidates ...Params) (interface{}
return nil, nil
}
func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) any) (any, string, map[string]any, error) {
keySegments := strings.Split(keyStr, separator)
if len(keySegments) == 0 {
return nil, "", nil, nil
@@ -211,7 +211,7 @@ func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interf
}
switch m := first.(type) {
case map[string]interface{}:
case map[string]any:
v, key, owner := getNested(m, keySegments[1:])
return v, key, owner, nil
case Params:
@@ -236,7 +236,7 @@ const (
mergeStrategyKey = "_merge"
)
func toMergeStrategy(v interface{}) ParamsMergeStrategy {
func toMergeStrategy(v any) ParamsMergeStrategy {
s := ParamsMergeStrategy(cast.ToString(v))
switch s {
case ParamsMergeStrategyDeep, ParamsMergeStrategyNone, ParamsMergeStrategyShallow:
@@ -260,13 +260,13 @@ func PrepareParams(m Params) {
retyped = true
} else {
switch vv := v.(type) {
case map[interface{}]interface{}:
case map[any]any:
var p Params = cast.ToStringMap(v)
v = p
PrepareParams(p)
retyped = true
case map[string]interface{}:
var p Params = v.(map[string]interface{})
case map[string]any:
var p Params = v.(map[string]any)
v = p
PrepareParams(p)
retyped = true
+7 -7
View File
@@ -20,13 +20,13 @@ import (
)
func TestGetNestedParam(t *testing.T) {
m := map[string]interface{}{
m := map[string]any{
"string": "value",
"first": 1,
"with_underscore": 2,
"nested": map[string]interface{}{
"nested": map[string]any{
"color": "blue",
"nestednested": map[string]interface{}{
"nestednested": map[string]any{
"color": "green",
},
},
@@ -34,7 +34,7 @@ func TestGetNestedParam(t *testing.T) {
c := qt.New(t)
must := func(keyStr, separator string, candidates ...Params) interface{} {
must := func(keyStr, separator string, candidates ...Params) any {
v, err := GetNestedParam(keyStr, separator, candidates...)
c.Assert(err, qt.IsNil)
return v
@@ -53,14 +53,14 @@ func TestGetNestedParam(t *testing.T) {
func TestGetNestedParamFnNestedNewKey(t *testing.T) {
c := qt.New(t)
nested := map[string]interface{}{
nested := map[string]any{
"color": "blue",
}
m := map[string]interface{}{
m := map[string]any{
"nested": nested,
}
existing, nestedKey, owner, err := GetNestedParamFn("nested.new", ".", func(key string) interface{} {
existing, nestedKey, owner, err := GetNestedParamFn("nested.new", ".", func(key string) any {
return m[key]
})
+14 -14
View File
@@ -24,7 +24,7 @@ import (
// Scratch is a writable context used for stateful operations in Page/Node rendering.
type Scratch struct {
values map[string]interface{}
values map[string]any
mu sync.RWMutex
}
@@ -50,8 +50,8 @@ func NewScratcher() Scratcher {
// Supports numeric values and strings.
//
// If the first add for a key is an array or slice, then the next value(s) will be appended.
func (c *Scratch) Add(key string, newAddend interface{}) (string, error) {
var newVal interface{}
func (c *Scratch) Add(key string, newAddend any) (string, error) {
var newVal any
c.mu.RLock()
existingAddend, found := c.values[key]
c.mu.RUnlock()
@@ -82,7 +82,7 @@ func (c *Scratch) Add(key string, newAddend interface{}) (string, error) {
// Set stores a value with the given key in the Node context.
// This value can later be retrieved with Get.
func (c *Scratch) Set(key string, value interface{}) string {
func (c *Scratch) Set(key string, value any) string {
c.mu.Lock()
c.values[key] = value
c.mu.Unlock()
@@ -98,7 +98,7 @@ func (c *Scratch) Delete(key string) string {
}
// Get returns a value previously set by Add or Set.
func (c *Scratch) Get(key string) interface{} {
func (c *Scratch) Get(key string) any {
c.mu.RLock()
val := c.values[key]
c.mu.RUnlock()
@@ -109,7 +109,7 @@ func (c *Scratch) Get(key string) interface{} {
// Values returns the raw backing map. Note that you should just use
// this method on the locally scoped Scratch instances you obtain via newScratch, not
// .Page.Scratch etc., as that will lead to concurrency issues.
func (c *Scratch) Values() map[string]interface{} {
func (c *Scratch) Values() map[string]any {
c.mu.RLock()
defer c.mu.RUnlock()
return c.values
@@ -117,14 +117,14 @@ func (c *Scratch) Values() map[string]interface{} {
// SetInMap stores a value to a map with the given key in the Node context.
// This map can later be retrieved with GetSortedMapValues.
func (c *Scratch) SetInMap(key string, mapKey string, value interface{}) string {
func (c *Scratch) SetInMap(key string, mapKey string, value any) string {
c.mu.Lock()
_, found := c.values[key]
if !found {
c.values[key] = make(map[string]interface{})
c.values[key] = make(map[string]any)
}
c.values[key].(map[string]interface{})[mapKey] = value
c.values[key].(map[string]any)[mapKey] = value
c.mu.Unlock()
return ""
}
@@ -134,14 +134,14 @@ func (c *Scratch) DeleteInMap(key string, mapKey string) string {
c.mu.Lock()
_, found := c.values[key]
if found {
delete(c.values[key].(map[string]interface{}), mapKey)
delete(c.values[key].(map[string]any), mapKey)
}
c.mu.Unlock()
return ""
}
// GetSortedMapValues returns a sorted map previously filled with SetInMap.
func (c *Scratch) GetSortedMapValues(key string) interface{} {
func (c *Scratch) GetSortedMapValues(key string) any {
c.mu.RLock()
if c.values[key] == nil {
@@ -149,7 +149,7 @@ func (c *Scratch) GetSortedMapValues(key string) interface{} {
return nil
}
unsortedMap := c.values[key].(map[string]interface{})
unsortedMap := c.values[key].(map[string]any)
c.mu.RUnlock()
var keys []string
for mapKey := range unsortedMap {
@@ -158,7 +158,7 @@ func (c *Scratch) GetSortedMapValues(key string) interface{} {
sort.Strings(keys)
sortedArray := make([]interface{}, len(unsortedMap))
sortedArray := make([]any, len(unsortedMap))
for i, mapKey := range keys {
sortedArray[i] = unsortedMap[mapKey]
}
@@ -168,5 +168,5 @@ func (c *Scratch) GetSortedMapValues(key string) interface{} {
// NewScratch returns a new instance of Scratch.
func NewScratch() *Scratch {
return &Scratch{values: make(map[string]interface{})}
return &Scratch{values: make(map[string]any)}
}
+4 -4
View File
@@ -90,7 +90,7 @@ func TestScratchAddTypedSliceToInterfaceSlice(t *testing.T) {
c := qt.New(t)
scratch := NewScratch()
scratch.Set("slice", []interface{}{})
scratch.Set("slice", []any{})
_, err := scratch.Add("slice", []int{1, 2})
c.Assert(err, qt.IsNil)
@@ -107,7 +107,7 @@ func TestScratchAddDifferentTypedSliceToInterfaceSlice(t *testing.T) {
_, err := scratch.Add("slice", []int{1, 2})
c.Assert(err, qt.IsNil)
c.Assert(scratch.Get("slice"), qt.DeepEquals, []interface{}{"foo", 1, 2})
c.Assert(scratch.Get("slice"), qt.DeepEquals, []any{"foo", 1, 2})
}
func TestScratchSet(t *testing.T) {
@@ -185,7 +185,7 @@ func TestScratchSetInMap(t *testing.T) {
scratch.SetInMap("key", "zyx", "Zyx")
scratch.SetInMap("key", "abc", "Abc (updated)")
scratch.SetInMap("key", "def", "Def")
c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, []interface{}{0: "Abc (updated)", 1: "Def", 2: "Lux", 3: "Zyx"})
c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, []any{0: "Abc (updated)", 1: "Def", 2: "Lux", 3: "Zyx"})
}
func TestScratchDeleteInMap(t *testing.T) {
@@ -199,7 +199,7 @@ func TestScratchDeleteInMap(t *testing.T) {
scratch.DeleteInMap("key", "abc")
scratch.SetInMap("key", "def", "Def")
scratch.DeleteInMap("key", "lmn") // Do nothing
c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, []interface{}{0: "Def", 1: "Lux", 2: "Zyx"})
c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, []any{0: "Def", 1: "Lux", 2: "Zyx"})
}
func TestScratchGetSortedMapValues(t *testing.T) {
+1 -1
View File
@@ -20,7 +20,7 @@ import (
// DoArithmetic performs arithmetic operations (+,-,*,/) using reflection to
// determine the type of the two terms.
func DoArithmetic(a, b interface{}, op rune) (interface{}, error) {
func DoArithmetic(a, b any, op rune) (any, error) {
av := reflect.ValueOf(a)
bv := reflect.ValueOf(b)
var ai, bi int64
+3 -3
View File
@@ -24,10 +24,10 @@ func TestDoArithmetic(t *testing.T) {
c := qt.New(t)
for _, test := range []struct {
a interface{}
b interface{}
a any
b any
op rune
expect interface{}
expect any
}{
{3, 2, '+', int64(5)},
{3, 2, '-', int64(1)},
+9
View File
@@ -63,6 +63,15 @@ func (filepathBridge) Separator() string {
var fpb filepathBridge
// AbsPathify creates an absolute path if given a working dir and a relative path.
// If already absolute, the path is just cleaned.
func AbsPathify(workingDir, inPath string) string {
if filepath.IsAbs(inPath) {
return filepath.Clean(inPath)
}
return filepath.Join(workingDir, inPath)
}
// MakeTitle converts the path given to a suitable title, trimming whitespace
// and replacing hyphens with whitespace.
func MakeTitle(inpath string) string {
+1 -1
View File
@@ -24,7 +24,7 @@ func TestGetRelativePath(t *testing.T) {
tests := []struct {
path string
base string
expect interface{}
expect any
}{
{filepath.FromSlash("/a/b"), filepath.FromSlash("/a"), filepath.FromSlash("b")},
{filepath.FromSlash("/a/b/c/"), filepath.FromSlash("/a"), filepath.FromSlash("b/c/")},
+27
View File
@@ -17,6 +17,7 @@ import (
"fmt"
"net/url"
"path"
"path/filepath"
"strings"
)
@@ -152,3 +153,29 @@ func Uglify(in string) string {
// /section/name.html -> /section/name.html
return path.Clean(in)
}
// UrlToFilename converts the URL s to a filename.
// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned.
func UrlToFilename(s string) (string, bool) {
u, err := url.ParseRequestURI(s)
if err != nil {
return filepath.FromSlash(s), false
}
p := u.Path
if p == "" {
p, _ = url.QueryUnescape(u.Opaque)
return filepath.FromSlash(p), true
}
p = filepath.FromSlash(p)
if u.Host != "" {
// C:\data\file.txt
p = strings.ToUpper(u.Host) + ":" + p
}
return p, true
}
+10 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2018 The Hugo Authors. All rights reserved.
// 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.
@@ -29,6 +29,15 @@ const (
noticeColor = "\033[1;36m%s\033[0m"
)
// PrintANSIColors returns false if NO_COLOR env variable is set,
// else IsTerminal(f).
func PrintANSIColors(f *os.File) bool {
if os.Getenv("NO_COLOR") != "" {
return false
}
return IsTerminal(f)
}
// IsTerminal return true if the file descriptor is terminal and the TERM
// environment variable isn't a dumb one.
func IsTerminal(f *os.File) bool {
+2 -2
View File
@@ -69,7 +69,7 @@ func createPositionStringFormatter(formatStr string) func(p Position) string {
format := replacer.Replace(formatStr)
f := func(pos Position) string {
args := make([]interface{}, len(identifiersFound))
args := make([]any, len(identifiersFound))
for i, id := range identifiersFound {
switch id {
case ":file":
@@ -83,7 +83,7 @@ func createPositionStringFormatter(formatStr string) func(p Position) string {
msg := fmt.Sprintf(format, args...)
if terminal.IsTerminal(os.Stdout) {
if terminal.PrintANSIColors(os.Stdout) {
return terminal.Notice(msg)
}
+32 -1
View File
@@ -14,6 +14,7 @@
package text
import (
"strings"
"sync"
"unicode"
@@ -23,7 +24,7 @@ import (
)
var accentTransformerPool = &sync.Pool{
New: func() interface{} {
New: func() any {
return transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
},
}
@@ -45,3 +46,33 @@ func RemoveAccentsString(s string) string {
accentTransformerPool.Put(t)
return s
}
// Chomp removes trailing newline characters from s.
func Chomp(s string) string {
return strings.TrimRightFunc(s, func(r rune) bool {
return r == '\n' || r == '\r'
})
}
// Puts adds a trailing \n none found.
func Puts(s string) string {
if s == "" || s[len(s)-1] == '\n' {
return s
}
return s + "\n"
}
// VisitLinesAfter calls the given function for each line, including newlines, in the given string.
func VisitLinesAfter(s string, fn func(line string)) {
high := strings.IndexRune(s, '\n')
for high != -1 {
fn(s[:high+1])
s = s[high+1:]
high = strings.IndexRune(s, '\n')
}
if s != "" {
fn(s)
}
}
+48
View File
@@ -26,3 +26,51 @@ func TestRemoveAccents(t *testing.T) {
c.Assert(string(RemoveAccents([]byte("Hugo Rocks!"))), qt.Equals, "Hugo Rocks!")
c.Assert(string(RemoveAccentsString("Resumé")), qt.Equals, "Resume")
}
func TestChomp(t *testing.T) {
c := qt.New(t)
c.Assert(Chomp("\nA\n"), qt.Equals, "\nA")
c.Assert(Chomp("A\r\n"), qt.Equals, "A")
}
func TestPuts(t *testing.T) {
c := qt.New(t)
c.Assert(Puts("A"), qt.Equals, "A\n")
c.Assert(Puts("\nA\n"), qt.Equals, "\nA\n")
c.Assert(Puts(""), qt.Equals, "")
}
func TestVisitLinesAfter(t *testing.T) {
const lines = `line 1
line 2
line 3`
var collected []string
VisitLinesAfter(lines, func(s string) {
collected = append(collected, s)
})
c := qt.New(t)
c.Assert(collected, qt.DeepEquals, []string{"line 1\n", "line 2\n", "\n", "line 3"})
}
func BenchmarkVisitLinesAfter(b *testing.B) {
const lines = `line 1
line 2
line 3`
for i := 0; i < b.N; i++ {
VisitLinesAfter(lines, func(s string) {
})
}
}
+7 -7
View File
@@ -25,13 +25,13 @@ import (
// ToDuration converts v to time.Duration.
// See ToDurationE if you need to handle errors.
func ToDuration(v interface{}) time.Duration {
func ToDuration(v any) time.Duration {
d, _ := ToDurationE(v)
return d
}
// ToDurationE converts v to time.Duration.
func ToDurationE(v interface{}) (time.Duration, error) {
func ToDurationE(v any) (time.Duration, error) {
if n := cast.ToInt(v); n > 0 {
return time.Duration(n) * time.Millisecond, nil
}
@@ -44,14 +44,14 @@ func ToDurationE(v interface{}) (time.Duration, error) {
// ToStringSlicePreserveString is the same as ToStringSlicePreserveStringE,
// but it never fails.
func ToStringSlicePreserveString(v interface{}) []string {
func ToStringSlicePreserveString(v any) []string {
vv, _ := ToStringSlicePreserveStringE(v)
return vv
}
// ToStringSlicePreserveStringE converts v to a string slice.
// If v is a string, it will be wrapped in a string slice.
func ToStringSlicePreserveStringE(v interface{}) ([]string, error) {
func ToStringSlicePreserveStringE(v any) ([]string, error) {
if v == nil {
return nil, nil
}
@@ -86,7 +86,7 @@ func ToStringSlicePreserveStringE(v interface{}) ([]string, error) {
// TypeToString converts v to a string if it's a valid string type.
// Note that this will not try to convert numeric values etc.,
// use ToString for that.
func TypeToString(v interface{}) (string, bool) {
func TypeToString(v any) (string, bool) {
switch s := v.(type) {
case string:
return s, true
@@ -110,13 +110,13 @@ func TypeToString(v interface{}) (string, bool) {
}
// ToString converts v to a string.
func ToString(v interface{}) string {
func ToString(v any) string {
s, _ := ToStringE(v)
return s
}
// ToStringE converts v to a string.
func ToStringE(v interface{}) (string, error) {
func ToStringE(v any) (string, error) {
if s, ok := TypeToString(v); ok {
return s, nil
}
+1 -1
View File
@@ -26,7 +26,7 @@ func TestToStringSlicePreserveString(t *testing.T) {
c.Assert(ToStringSlicePreserveString("Hugo"), qt.DeepEquals, []string{"Hugo"})
c.Assert(ToStringSlicePreserveString(qt.Commentf("Hugo")), qt.DeepEquals, []string{"Hugo"})
c.Assert(ToStringSlicePreserveString([]interface{}{"A", "B"}), qt.DeepEquals, []string{"A", "B"})
c.Assert(ToStringSlicePreserveString([]any{"A", "B"}), qt.DeepEquals, []string{"A", "B"})
c.Assert(ToStringSlicePreserveString([]int{1, 3}), qt.DeepEquals, []string{"1", "3"})
c.Assert(ToStringSlicePreserveString(nil), qt.IsNil)
}
+20
View File
@@ -0,0 +1,20 @@
// 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 hstring
type RenderedString string
func (s RenderedString) String() string {
return string(s)
}
@@ -1,4 +1,4 @@
// Copyright 2017 The Hugo Authors. All rights reserved.
// 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.
@@ -11,32 +11,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package math
package hstring
import (
"html/template"
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/htesting/hqt"
"github.com/gohugoio/hugo/tpl/internal"
"github.com/spf13/cast"
)
func TestInit(t *testing.T) {
func TestRenderedString(t *testing.T) {
c := qt.New(t)
var found bool
var ns *internal.TemplateFuncsNamespace
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
ns = nsf(&deps.Deps{})
if ns.Name == name {
found = true
break
}
}
c.Assert(found, qt.Equals, true)
ctx, err := ns.Context()
c.Assert(err, qt.IsNil)
c.Assert(ctx, hqt.IsSameType, &Namespace{})
// Validate that it will behave like a string in Hugo settings.
c.Assert(cast.ToString(RenderedString("Hugo")), qt.Equals, "Hugo")
c.Assert(template.HTML(RenderedString("Hugo")), qt.Equals, template.HTML("Hugo"))
}
+6 -6
View File
@@ -29,8 +29,8 @@ type RLocker interface {
// KeyValue is a interface{} tuple.
type KeyValue struct {
Key interface{}
Value interface{}
Key any
Value any
}
// KeyValueStr is a string tuple.
@@ -41,8 +41,8 @@ type KeyValueStr struct {
// KeyValues holds an key and a slice of values.
type KeyValues struct {
Key interface{}
Values []interface{}
Key any
Values []any
}
// KeyString returns the key as a string, an empty string if conversion fails.
@@ -57,7 +57,7 @@ func (k KeyValues) String() string {
// NewKeyValuesStrings takes a given key and slice of values and returns a new
// KeyValues struct.
func NewKeyValuesStrings(key string, values ...string) KeyValues {
iv := make([]interface{}, len(values))
iv := make([]any, len(values))
for i := 0; i < len(values); i++ {
iv[i] = values[i]
}
@@ -71,7 +71,7 @@ type Zeroer interface {
}
// IsNil reports whether v is nil.
func IsNil(v interface{}) bool {
func IsNil(v any) bool {
if v == nil {
return true
}
+1 -1
View File
@@ -25,5 +25,5 @@ func TestKeyValues(t *testing.T) {
kv := NewKeyValuesStrings("key", "a1", "a2")
c.Assert(kv.KeyString(), qt.Equals, "key")
c.Assert(kv.Values, qt.DeepEquals, []interface{}{"a1", "a2"})
c.Assert(kv.Values, qt.DeepEquals, []any{"a1", "a2"})
}
+2 -2
View File
@@ -17,6 +17,6 @@ package urls
// args must contain a path, but can also point to the target
// language or output format.
type RefLinker interface {
Ref(args map[string]interface{}) (string, error)
RelRef(args map[string]interface{}) (string, error)
Ref(args map[string]any) (string, error)
RelRef(args map[string]any) (string, error)
}
+6 -3
View File
@@ -17,13 +17,16 @@ package compare
// The semantics of equals is that the two value are interchangeable
// in the Hugo templates.
type Eqer interface {
Eq(other interface{}) bool
// Eq returns whether this value is equal to the other.
// This is for internal use.
Eq(other any) bool
}
// ProbablyEqer is an equal check that may return false positives, but never
// a false negative.
type ProbablyEqer interface {
ProbablyEq(other interface{}) bool
// For internal use.
ProbablyEq(other any) bool
}
// Comparer can be used to compare two values.
@@ -31,5 +34,5 @@ type ProbablyEqer interface {
// Compare returns -1 if the given version is less than, 0 if equal and 1 if greater than
// the running version.
type Comparer interface {
Compare(other interface{}) int
Compare(other any) int
}
+19
View File
@@ -62,3 +62,22 @@ func TestLexicographicSort(t *testing.T) {
c.Assert(s, qt.DeepEquals, []string{"A", "b", "Ba", "ba", "ba", "Bz"})
}
func BenchmarkStringSort(b *testing.B) {
prototype := []string{"b", "Bz", "zz", "ba", "αβδ αβδ αβδ", "A", "Ba", "ba", "nnnnasdfnnn", "AAgæåz", "αβδC"}
b.Run("LessStrings", func(b *testing.B) {
ss := make([][]string, b.N)
for i := 0; i < b.N; i++ {
ss[i] = make([]string, len(prototype))
copy(ss[i], prototype)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
sss := ss[i]
sort.Slice(sss, func(i, j int) bool {
return LessStrings(sss[i], sss[j])
})
}
})
}
+33 -14
View File
@@ -14,12 +14,11 @@
package config
import (
"fmt"
"sort"
"strings"
"sync"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/common/types"
"github.com/gobwas/glob"
@@ -87,7 +86,7 @@ type Sitemap struct {
Filename string
}
func DecodeSitemap(prototype Sitemap, input map[string]interface{}) Sitemap {
func DecodeSitemap(prototype Sitemap, input map[string]any) Sitemap {
for key, value := range input {
switch key {
case "changefreq":
@@ -177,14 +176,19 @@ func (s *Server) MatchRedirect(pattern string) Redirect {
type Headers struct {
For string
Values map[string]interface{}
Values map[string]any
}
type Redirect struct {
From string
To string
From string
To string
// HTTP status code to use for the redirect.
// A status code of 200 will trigger a URL rewrite.
Status int
Force bool
// Forcode redirect, even if original request path exists.
Force bool
}
func (r Redirect) IsZero() bool {
@@ -201,16 +205,31 @@ func DecodeServer(cfg Provider) (*Server, error) {
_ = mapstructure.WeakDecode(m, s)
for i, redir := range s.Redirects {
// Get it in line with the Hugo server.
redir.To = strings.TrimSuffix(redir.To, "index.html")
if !strings.HasPrefix(redir.To, "https") && !strings.HasSuffix(redir.To, "/") {
// There are some tricky infinite loop situations when dealing
// when the target does not have a trailing slash.
// This can certainly be handled better, but not time for that now.
return nil, errors.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To)
// Get it in line with the Hugo server for OK responses.
// We currently treat the 404 as a special case, they are always "ugly", so keep them as is.
if redir.Status != 404 {
redir.To = strings.TrimSuffix(redir.To, "index.html")
if !strings.HasPrefix(redir.To, "https") && !strings.HasSuffix(redir.To, "/") {
// There are some tricky infinite loop situations when dealing
// when the target does not have a trailing slash.
// This can certainly be handled better, but not time for that now.
return nil, fmt.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To)
}
}
s.Redirects[i] = redir
}
if len(s.Redirects) == 0 {
// Set up a default redirect for 404s.
s.Redirects = []Redirect{
{
From: "**",
To: "/404.html",
Status: 404,
},
}
}
return s, nil
}
+2 -2
View File
@@ -27,7 +27,7 @@ func TestBuild(t *testing.T) {
c := qt.New(t)
v := New()
v.Set("build", map[string]interface{}{
v.Set("build", map[string]any{
"useResourceCacheWhen": "always",
})
@@ -35,7 +35,7 @@ func TestBuild(t *testing.T) {
c.Assert(b.UseResourceCacheWhen, qt.Equals, "always")
v.Set("build", map[string]interface{}{
v.Set("build", map[string]any{
"useResourceCacheWhen": "foo",
})
+4 -4
View File
@@ -47,7 +47,7 @@ func (c *compositeConfig) GetInt(key string) int {
return c.base.GetInt(key)
}
func (c *compositeConfig) Merge(key string, value interface{}) {
func (c *compositeConfig) Merge(key string, value any) {
c.layer.Merge(key, value)
}
@@ -58,7 +58,7 @@ func (c *compositeConfig) GetParams(key string) maps.Params {
return c.base.GetParams(key)
}
func (c *compositeConfig) GetStringMap(key string) map[string]interface{} {
func (c *compositeConfig) GetStringMap(key string) map[string]any {
if c.layer.IsSet(key) {
return c.layer.GetStringMap(key)
}
@@ -79,7 +79,7 @@ func (c *compositeConfig) GetStringSlice(key string) []string {
return c.base.GetStringSlice(key)
}
func (c *compositeConfig) Get(key string) interface{} {
func (c *compositeConfig) Get(key string) any {
if c.layer.IsSet(key) {
return c.layer.Get(key)
}
@@ -100,7 +100,7 @@ func (c *compositeConfig) GetString(key string) string {
return c.base.GetString(key)
}
func (c *compositeConfig) Set(key string, value interface{}) {
func (c *compositeConfig) Set(key string, value any) {
c.layer.Set(key, value)
}
+16 -10
View File
@@ -14,14 +14,13 @@
package config
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/gohugoio/hugo/common/herrors"
"github.com/pkg/errors"
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/maps"
@@ -60,18 +59,25 @@ func FromConfigString(config, configType string) (Provider, error) {
func FromFile(fs afero.Fs, filename string) (Provider, error) {
m, err := loadConfigFromFile(fs, filename)
if err != nil {
return nil, herrors.WithFileContextForFileDefault(err, filename, fs)
fe := herrors.UnwrapFileError(err)
if fe != nil {
pos := fe.Position()
pos.Filename = filename
fe.UpdatePosition(pos)
return nil, err
}
return nil, herrors.NewFileErrorFromFile(err, filename, fs, nil)
}
return NewFrom(m), nil
}
// FromFileToMap is the same as FromFile, but it returns the config values
// as a simple map.
func FromFileToMap(fs afero.Fs, filename string) (map[string]interface{}, error) {
func FromFileToMap(fs afero.Fs, filename string) (map[string]any, error) {
return loadConfigFromFile(fs, filename)
}
func readConfig(format metadecoders.Format, data []byte) (map[string]interface{}, error) {
func readConfig(format metadecoders.Format, data []byte) (map[string]any, error) {
m, err := metadecoders.Default.UnmarshalToMap(data, format)
if err != nil {
return nil, err
@@ -82,7 +88,7 @@ func readConfig(format metadecoders.Format, data []byte) (map[string]interface{}
return m, nil
}
func loadConfigFromFile(fs afero.Fs, filename string) (map[string]interface{}, error) {
func loadConfigFromFile(fs afero.Fs, filename string) (map[string]any, error) {
m, err := metadecoders.Default.UnmarshalFileToMap(fs, filename)
if err != nil {
return nil, err
@@ -132,7 +138,7 @@ func LoadConfigFromDir(sourceFs afero.Fs, configDir, environment string) (Provid
if err != nil {
// This will be used in error reporting, use the most specific value.
dirnames = []string{path}
return errors.Wrapf(err, "failed to unmarshl config for path %q", path)
return fmt.Errorf("failed to unmarshl config for path %q: %w", path, err)
}
var keyPath []string
@@ -156,13 +162,13 @@ func LoadConfigFromDir(sourceFs afero.Fs, configDir, environment string) (Provid
root := item
if len(keyPath) > 0 {
root = make(map[string]interface{})
root = make(map[string]any)
m := root
for i, key := range keyPath {
if i >= len(keyPath)-1 {
m[key] = item
} else {
nm := make(map[string]interface{})
nm := make(map[string]any)
m[key] = nm
m = nm
}
@@ -203,6 +209,6 @@ func init() {
// RenameKeys renames config keys in m recursively according to a global Hugo
// alias definition.
func RenameKeys(m map[string]interface{}) {
func RenameKeys(m map[string]any) {
keyAliases.Rename(m)
}

Some files were not shown because too many files have changed in this diff Show More