Compare commits

..

512 Commits

Author SHA1 Message Date
Bjørn Erik Pedersen 3fbcc4b7b3 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 14: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 65e52a7f5c Merge commit 'bdf935d66c1f02dfc942a30e9fc00519bba3aacb' 2022-06-28 20:51:33 +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
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
hugoreleaser cdf6a0d622 releaser: Bump versions for release of 0.92.2
[ci skip]
2022-02-11 14:17:39 +00:00
hugoreleaser bf1fa71379 releaser: Add release notes for 0.92.2
[ci skip]
2022-02-11 14:17:38 +00:00
Joe Mooring 4f4cec73be Add HUGO_ENV to the os/exec environment
Closes #9490
2022-02-11 08:30:26 +01:00
Bjørn Erik Pedersen da4866c2be Simplify some integration tests 2022-02-10 20:43:19 +01:00
Joe Mooring d1109f590a Fix validation of Page Kind in cascade target map
Fixes #8888
2022-02-10 20:42:31 +01:00
Bjørn Erik Pedersen a7d182cea1 Add another cascade benchmark 2022-02-10 20:40:49 +01:00
Bjørn Erik Pedersen a2a660ed12 commands: Fix server deadlock on config error
Fixes #9486
2022-02-10 19:43:47 +01:00
Joe Mooring f7bc4cc505 Exclude event attributes when rendering markdown
Closes #9463
2022-02-10 18:25:19 +01:00
Joe Mooring 54f8d8a70a Remove the "check" command
Closes #9454
2022-02-10 16:25:23 +01:00
Joe Mooring 3036d0ac9a Update the application/javascript media type
Add jsm and mjs extensions.
Closes #9483
2022-02-10 11:51:28 +01:00
Bjørn Erik Pedersen 6a238a7276 tpl/templates: Fix templates.Exist issue with base templates
Fixes #9477
2022-02-09 22:33:07 +01:00
Bjørn Erik Pedersen f60714b5a1 Add a migration test helper 2022-02-09 16:38:12 +01:00
Bjørn Erik Pedersen 215a715ddd babel: Port integration tests to their own package 2022-02-09 15:41:32 +01:00
Bjørn Erik Pedersen d128d260b5 js: Port integration tests to its own package 2022-02-09 15:41:32 +01:00
Bjørn Erik Pedersen c4aaf1d516 postcss: Move integration test to its own package 2022-02-09 15:41:32 +01:00
Bjørn Erik Pedersen 94f10cf4f9 minifier: Port integration tests to its package 2022-02-09 15:41:32 +01:00
Bjørn Erik Pedersen b06c2103b8 templates: Port integration test to its package 2022-02-09 15:41:32 +01:00
Bjørn Erik Pedersen d22f7795c7 tocss: Port integration tests to their package 2022-02-09 15:41:32 +01:00
Bjørn Erik Pedersen 39f69ca7f0 openapi3: Port integration test into correct package 2022-02-09 15:41:32 +01:00
Bjørn Erik Pedersen 64f75adcf6 Add a new integration test framework
I have had this living in a separate branch for now, but we need this in the main branch sooner rather than later.

One big advantage of this is that integration tests can live in any package, not just hugolib.
2022-02-09 15:41:32 +01:00
Joe Mooring 9262719092 Validate comparison operator argument count
Fixes #9462
2022-02-05 17:41:43 +01:00
Joe Mooring 3336762939 Remove hugo gen autocomplete
Closes #8862
2022-02-01 23:23:30 +01:00
Bjørn Erik Pedersen 5ca40c8f7c deps: Update github.com/pelletier/go-toml/v2 to v2.0.0-beta.6
Closes #9439
Closes #9417
2022-01-29 13:55:23 +01:00
Bjørn Erik Pedersen ef7d14a241 Fix erroneous warning with .Page.RenderString on a page without a backing file
Fixes #9433
2022-01-28 17:35:49 +01:00
Joe Mooring c05c99f0c5 Fix typo in panicOnWarning message 2022-01-27 17:50:34 +01:00
hugoreleaser ff7689ce02 releaser: Prepare repository for 0.93.0-DEV
[ci skip]
2022-01-27 12:22:31 +00:00
hugoreleaser 85e2e86266 releaser: Bump versions for release of 0.92.1
[ci skip]
2022-01-27 11:44:40 +00:00
hugoreleaser 515f8a6d8e releaser: Add release notes for 0.92.1
[ci skip]
2022-01-27 11:44:40 +00:00
Bjørn Erik Pedersen f22c4aba04 Make the RenderString content provider fix more general
Updates #9383
2022-01-27 11:51:13 +01:00
Paul Gottschling 85d31f7bfb Fix .RenderString issue in .Translations
Fixes #9383
2022-01-27 11:51:13 +01:00
Ephex2 22055176d2 general: Fix issue causing log threads to hang indefinitely when print() panics
The function printIfNotPrinted() defined for DistinctLogger unlocked the mutex within
the logger only after the print() function ran. If print panics, the mutex would stay
locked and future attempts to read or write from the logger mutex would cause the goroutine
to hang indefinitely.

Deferred the unlocking of the mutex to prevent this. Also, put l.m[key] before the print()
call since this will prevent another bug where the same warning potentially gets logged
multiple times if the print() call panics.

Fixes #9380
2022-01-27 10:38:50 +01:00
Bjørn Erik Pedersen 7a080b624e Fix duplicate mount sources
Fixes #9426
2022-01-26 20:32:13 +01:00
Bjørn Erik Pedersen 2655739940 tpl/collections: Fix apply with namespaced template funcs
We changed the signature to `func(...interface{}) (interface{}, error)` some time ago, but sadly we had no test for this for `apply`. Now we do.

Fixes #9393
2022-01-17 09:50:48 +01:00
Bjørn Erik Pedersen 348d300a71 common: Remove unused code 2022-01-16 17:12:56 +01:00
Bjørn Erik Pedersen 6f07bdb152 common/paths: Remove unused code 2022-01-16 17:12:56 +01:00
Bjørn Erik Pedersen 55a9bc1e70 helpers: Remove unused code 2022-01-16 17:12:56 +01:00
Joe Mooring 20a7ce7c1b Do not render hl_style as an HTML attribute
Fixes #9390
2022-01-16 17:09:18 +01:00
dependabot[bot] 8cd4492407 build(deps): bump github.com/spf13/viper from 1.8.1 to 1.10.1
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.8.1 to 1.10.1.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.8.1...v1.10.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-14 19:04:32 -07:00
Andreas Deininger 9d8f318aab Fixing typos (#9387) 2022-01-14 16:07:17 -08:00
Andreas Deininger fcbbbef22d Fix typo in warning message 2022-01-13 12:21:13 +01:00
Bjørn Erik Pedersen 6041adc164 github: Clean up the issue templates a little 2022-01-13 09:44:55 +01:00
Bjørn Erik Pedersen 408da43657 github: Add lock-threads step 2022-01-12 19:55:53 +01:00
hugoreleaser ed04ed5745 releaser: Prepare repository for 0.93.0-DEV
[ci skip]
2022-01-12 08:58:07 +00:00
hugoreleaser b354940369 releaser: Bump versions for release of 0.92.0
[ci skip]
2022-01-12 08:23:17 +00:00
hugoreleaser bd89aef8ba releaser: Add release notes for 0.92.0
[ci skip]
2022-01-12 08:23:17 +00:00
Bjørn Erik Pedersen f2bc13dd9e docs: Regenerate docshelper 2022-01-12 08:17:03 +01:00
Bjørn Erik Pedersen 098254f175 Merge commit 'a8e9fc699a6ff7d578f97a7c553ce844efad8fdb' 2022-01-12 08:16:35 +01:00
Bjørn Erik Pedersen a8e9fc699a Squashed 'docs/' changes from 4eb10c1a9..b8b20e9a2
b8b20e9a2 Update index.md
f33994fe6 Remove files.Extension (duplicate of files.Ext)
948d6d69d layouts: Use .File.Path
d3050b78c Document .Page.BundleType (#1620)
8a033918f Image filters: ensure Grayscale is a level-2 heading
98537018f Document .Publish method for global resources
963ddc994 docs: add a link to the mailmap documentation
915f858dc Fix release notes version
58093dafe Update index.md
8008ba1e1 Release 0.91.2
d1788dae8 Merge branch 'tempv0.91.2'
af2970180 Revert "config/security: Add HOME to default exec env var whitelist"
2648d3088 netlify: Hugo 0.91.1
d0801599c Merge branch 'tempv0.91.1'
b343bfd7a config/security: Add HOME to default exec env var whitelist
03fbb403f Update data-templates.md
2f608055f Correct GetRemote docs and examples
4e942166a Update 2021-12-17-no-more-releasenotes.md
dbf9514fd Update security.toml
2c38aa356 Update index.md
562ad8e96 Add timeZone
4bc482152 Update introduction.md
1eb66c758 news: Add a note about the placement of release notes
b2a293abb Remove the default archetype template
f9837793c netlify: Hugo 0.91.0
467256ad5 docs: Regen docs helper
68554cf77 Add some basic security policies with sensible defaults

git-subtree-dir: docs
git-subtree-split: b8b20e9a25
2022-01-12 08:15:10 +01:00
Bjørn Erik Pedersen cdcd15b6c2 Only create LazyContentProvider for the non-rendering Site
Which saves a fair amound of allocations:

```
gobench --package ./hugolib --bench "SiteNew/Regular_D" --base master
```

Before:

```
name                                  old time/op    new time/op    delta
SiteNew/Regular_Deep_content_tree-10    40.7ms ± 3%    41.2ms ± 1%    ~     (p=0.343 n=4+4)

name                                  old alloc/op   new alloc/op   delta
SiteNew/Regular_Deep_content_tree-10    27.7MB ± 0%    28.8MB ± 0%  +3.76%  (p=0.029 n=4+4)

name                                  old allocs/op  new allocs/op  delta
SiteNew/Regular_Deep_content_tree-10      304k ± 0%      329k ± 0%  +8.07%  (p=0.029 n=4+4)
```

After:

```
name                                  old time/op    new time/op    delta
SiteNew/Regular_Deep_content_tree-10    34.2ms ± 1%    34.7ms ± 1%    ~     (p=0.114 n=4+4)

name                                  old alloc/op   new alloc/op   delta
SiteNew/Regular_Deep_content_tree-10    27.7MB ± 0%    28.1MB ± 0%  +1.38%  (p=0.029 n=4+4)

name                                  old allocs/op  new allocs/op  delta
SiteNew/Regular_Deep_content_tree-10      304k ± 0%      314k ± 0%  +3.03%  (p=0.029 n=4+4)
```

Updates #8919
2022-01-12 07:45:53 +01:00
Paul Gottschling 25d645f47a Fix missing page data for alternative formats
When a template calls the .Translations function and a
Hugo environment is using multiple output formats,
a template that calls methods like .Summary and .Len on
each translation will unexpectedly show empty return
values for these methods.

This is because each pageOutput's ContentProvider is
assigned to a page.NopPage in newPageOutput. When
*HugoSites.render assigns pageContentOutputs to
pageOutputs in *pageState.shiftToOutputFormat, it
reuses pageContentOutputs from other pageOutputs,
leaving some pageContentOutputs as NopPages. While this
approach conserves resources, sometimes it means that
a template will unexpectedly call a method on a
pageContentOutput that is actually a NopPage.

In the case of ContentProvider methods called on
translations for alternative output formats, the methods
were called on NopPages.

This change introduces LazyContentProvider, which
performs late initialization when one of its methods is
called. This way, we can reuse content in "normal" cases
but ensure that ContentProvider methods work as expected
when a pageOutput is not assigned a pageContentOutput
during the initial pre-render phase.

Fixes #8919
2022-01-12 07:45:53 +01:00
Bjørn Erik Pedersen fbb3c181cb docs: Add dependency table to maintainance page
See #8949
2022-01-11 21:57:37 +01:00
Bjørn Erik Pedersen 9af4ca3861 deps: Upgrade github.com/evanw/esbuild v0.14.8 => v0.14.11 2022-01-11 20:31:50 +01:00
Bjørn Erik Pedersen 7396aa945a Add hugo.Deps
Fixes #8949
2022-01-11 18:06:23 +01:00
roointan d82cef5c53 hugolib: Fix livereload problem with files including NFC characters in MacOs
Fixes #8429
2022-01-08 11:35:30 +01:00
Bjørn Erik Pedersen 74f0777c5f docs. Regen CLI docs
Closes #9363
2022-01-07 12:37:07 +01:00
Bjørn Erik Pedersen e334a40661 commands: Fix CLI help text for hugo new
Updates #9363
2022-01-07 12:36:12 +01:00
Bjørn Erik Pedersen 5bd3c8df40 Update to Go 1.17.6
Fixes #9361
2022-01-07 11:07:42 +01:00
Joe Mooring 0aca99fe01 create: Correctly pass newContentEditor flags
Fixes #9356
2022-01-07 10:46:32 +01:00
Bjørn Erik Pedersen c8b5ab75b7 Add --panicOnWarning flag
Fixes #9357
Fixes #9359
2022-01-06 12:27:04 +01:00
Bjørn Erik Pedersen 85c5b89593 github: Increase stale days 2022-01-05 15:48:00 +01:00
Bjørn Erik Pedersen 965760835d docs: Regenerate CLI docs 2022-01-04 17:12:09 +01:00
Bjørn Erik Pedersen 4a0b55330c docs: Regenerate docshelper 2022-01-04 17:11:17 +01:00
Bjørn Erik Pedersen 1651beb2c1 Remove mmark
Closes #9350
2022-01-04 17:10:39 +01:00
Bjørn Erik Pedersen 2b6063c3e3 Misc depreation updates
* Deprecate .Page.Path when backed by a file
* site.Permalinks
* --ignoreVendor (use --ignoreVendorPaths)

Closes #9348
Closes #9349
2022-01-04 17:10:39 +01:00
Bjørn Erik Pedersen 56ab83a597 Make the deprecated Page/File methods (from Hugo 0.55) ERROR
Closes #9346
2022-01-04 17:10:39 +01:00
Bjørn Erik Pedersen dad0dc8d86 github: Add add stale GitHub action 2022-01-04 17:08:10 +01:00
Paul Gottschling d3c4fdb8ff Fix surprise OutputFormat.Rel overwriting
In page.NewOutputFormat, we take an output.Format f and use it to
create a page.OutputFormat. If the format is canonical, we assign
the final OutputFormat's Rel to "canonical" rather than using
f.Rel. However, this leads to unexpected behavior for custom
output formats, where a user can define a "rel" for a format
via the config file.

For example, the standard for "humans.txt" files requires using
rel="author" in HTML "link" elements. Meanwhile, humans.txt is
usually the only format used for its content. As a result, for
Hugo configurations that define a humans.txt custom output format,
Hugo will render "link" elements to content in this format with
rel="canonical," rather than "author" as required by the standard.

This commit changes page.NewOutputFormat to check whether a given
format is user defined and, if so, skips assigning Rel to
"canonical," even if isCanonical is true.

Fixes #8030
2022-01-04 10:38:38 +01:00
Bjørn Erik Pedersen d632dd7d74 hugolib: Make an RST test optional
If no RST installed.
2022-01-01 12:20:07 +01:00
Joe Mooring 0671ef5593 deps: Upgrade github.com/niklasfasching/go-org v1.5.0 => v1.6.0
Fixes #8921
2021-12-30 22:40:43 +01:00
Bjørn Erik Pedersen 672481f1f0 Update stale.yml 2021-12-30 20:13:08 +01:00
hugoreleaser 1dbfc0f930 releaser: Prepare repository for 0.92.0-DEV
[ci skip]
2021-12-23 16:10:18 +00:00
hugoreleaser 1798bd3fd1 releaser: Bump versions for release of 0.91.2
[ci skip]
2021-12-23 15:33:33 +00:00
hugoreleaser f0b55a68ee releaser: Add release notes for 0.91.2
[ci skip]
2021-12-23 15:33:33 +00:00
Bjørn Erik Pedersen 623dda7174 Revert "config/security: Add HOME to default exec env var whitelist"
There have been one report in the wild suggesting that this needs to be tested better before doing:

https://discourse.gohugo.io/t/hugo-mod-failing-in-v0-91-1-but-works-in-v0-91-0/36180/5

This reverts commit fca266ebbb.
2021-12-23 16:23:15 +01:00
Bjørn Erik Pedersen aee9e11a40 Make sure we always create the /public folder
* Before this commit, when you had static files in the root of /content and no /public folder, that folder would not be created unless the /static syncer had already run.
* So, with a common pattern doing `rm -rf public && hugo` would the fail now and then because /static and /content are processed in parallel (unless you have cleanDestinationDir=true)
* This was even worse before commit 0b918e131f – a frozen build.

Closes #8166
2021-12-23 15:09:27 +01:00
Bjørn Erik Pedersen bd63c1aa56 Fix "stuck on build" in error situations in content processing
Updates #8166
2021-12-23 15:09:27 +01:00
Bjørn Erik Pedersen 9eb05807c6 deps: Run "go mod tidy" 2021-12-23 10:19:10 +01:00
Bjørn Erik Pedersen 654f513a4c deps: Upgrade github.com/evanw/esbuild v0.14.7 => v0.14.8 2021-12-23 10:19:10 +01:00
hugoreleaser 759cdf3fc8 releaser: Prepare repository for 0.92.0-DEV
[ci skip]
2021-12-22 16:07:34 +00:00
hugoreleaser f423505746 releaser: Bump versions for release of 0.91.1
[ci skip]
2021-12-22 15:31:05 +00:00
hugoreleaser af165d5b6d releaser: Add release notes for 0.91.1
[ci skip]
2021-12-22 15:31:05 +00:00
Bjørn Erik Pedersen 6779117f72 media: Also consider extension in FromContent
As used in `resources.GetRemote`.

This will now reject image files with text and text files with images.
2021-12-22 11:35:53 +01:00
Bjørn Erik Pedersen ce04011096 media: Add missing BMP and GIF to the default MediaTypes list 2021-12-22 11:35:53 +01:00
Bjørn Erik Pedersen cdc73526a8 media: Add PDF MIME type 2021-12-22 11:35:53 +01:00
Bjørn Erik Pedersen 425c7d90f0 deps: Update github.com/evanw/esbuild v0.14.5 => v0.14.7 2021-12-22 11:34:21 +01:00
Bjørn Erik Pedersen fca266ebbb config/security: Add HOME to default exec env var whitelist
See #9309
2021-12-22 11:33:59 +01:00
Bjørn Erik Pedersen 0016e21cd6 modules: Set GOCACHE env var
This was introduced in Go 1.15. We do set the GOPATH, which should be enough, but #9309 indicate that's not the case on every platform (GitHub Actions).

Closes #9309
2021-12-22 11:33:59 +01:00
hugoreleaser 728feaecf0 releaser: Prepare repository for 0.92.0-DEV
[ci skip]
2021-12-17 10:31:22 +00:00
hugoreleaser d1dc0e9a54 releaser: Bump versions for release of 0.91.0
[ci skip]
2021-12-17 09:50:19 +00:00
hugoreleaser 072bca69d8 releaser: Add release notes for 0.91.0
[ci skip]
2021-12-17 09:50:19 +00:00
Bjørn Erik Pedersen e26e13fbb7 releaser: Drop running tests as part of the release 2021-12-17 10:44:10 +01:00
Bjørn Erik Pedersen 6df2f080c9 docs: Regen docs helper 2021-12-17 10:31:08 +01:00
Bjørn Erik Pedersen b84745d497 tpl/resources: Add empty method mapping for GetRemote
To get auto generated documentation for it.
2021-12-17 10:30:10 +01:00
Bjørn Erik Pedersen 44954497bc Always use content to resolve content type in resources.GetRemote
This is a security hardening measure; don't trust the URL extension or any `Content-Type`/`Content-Disposition` header on its own, always look at the file content using Go's `http.DetectContentType`.

This commit also adds ttf and otf media type definitions to Hugo.

Fixes #9302
Fixes #9301
2021-12-17 09:50:28 +01:00
Bjørn Erik Pedersen 22ef5da20d Add resources.GetRemote
In Hugo 0.89 we added remote support to `resources.Get`.

In hindsight that was not a great idea, as a poll from many Hugo users showed. See Issue #9285 for more details.

After this commit `resources.Get` only supports local resource lookups. If you want to support both, you need to use a construct similar to:

Also improve some option case handling.

```
{{ resource := "" }}
{{ if (urls.Parse $url).IsAbs }}
{{ $resource = resources.GetRemote $url }}
{{ else }}
{{ $resource = resources.Get $url }}
{{ end }}
```

Fixes #9285
Fixes #9296
2021-12-17 09:33:51 +01:00
Paul Gottschling 5758c370ea Allow for return partials with falsy arguments (#9298)
Partials with returns values are parsed, then inserted into a
partial return wrapper via wrapInPartialReturnWrapper in order
to assign the return value via *contextWrapper.Set. The
predefined wrapper template for partials inserts a partial's nodes
into a "with" template action in order to set dot to a
*contextWrapper within the partial. However, because "with" is
skipped if its argument is falsy, partials with falsy arguments
were not being evaluated.

This replaces the "with" action in the partial wrapper with a
"range" action that isn't skipped if .Arg is falsy.

Fixes #7528
2021-12-17 08:35:21 +01:00
Bjørn Erik Pedersen 8ee6de6d96 deps: Upgrade github.com/evanw/esbuild v0.14.2 => v0.14.5 2021-12-17 08:26:45 +01:00
Joachim Ansorg a4b9f1a92c don't use path.Join, because it cleans the final path 2021-12-16 21:14:33 +01:00
Bjørn Erik Pedersen f4389e48ce Add some basic security policies with sensible defaults
This ommmit contains some security hardening measures for the Hugo build runtime.

There are some rarely used features in Hugo that would be good to have disabled by default. One example would be the "external helpers".

For `asciidoctor` and some others we use Go's `os/exec` package to start a new process.

These are a predefined set of binary names, all loaded from `PATH` and with a predefined set of arguments. Still, if you don't use `asciidoctor` in your project, you might as well have it turned off.

You can configure your own in the new `security` configuration section, but the defaults are configured to create a minimal amount of site breakage. And if that do happen, you will get clear instructions in the loa about what to do.

The default configuration is listed below. Note that almost all of these options are regular expression _whitelists_ (a string or a slice); the value `none` will block all.

```toml
[security]
  enableInlineShortcodes = false
  [security.exec]
    allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$']
    osEnv = ['(?i)^(PATH|PATHEXT|APPDATA|TMP|TEMP|TERM)$']

  [security.funcs]
    getenv = ['^HUGO_']

  [security.http]
    methods = ['(?i)GET|POST']
    urls = ['.*']
```
2021-12-16 09:40:22 +01:00
Bjørn Erik Pedersen 803f572e66 Simplify Babel test assertions
To make the tests pass on my grand old Windows 8 VM.
2021-12-14 07:26:53 +01:00
Bjørn Erik Pedersen 6183184b96 Merge commit '45e6fdb315d113ba13e20a633ed0c67e3f25170d' 2021-12-13 21:05:10 +01:00
Bjørn Erik Pedersen 45e6fdb315 Squashed 'docs/' changes from 316cec249..4eb10c1a9
4eb10c1a9 Set a sensible maxAge on the image cache
2c37f4f89 Update lists.md
8690c018f Update introduction.md
0458c3d88 Update index.md
150982f5e Update index.md
50dc0b6b9 Update multilingual.md
648598090 Configure image cache to use :cacheDir/images
4dfb523c3 Update introduction.md
b94d42e6f Add text to social images
d3253d2aa Add resources/_gen/images/ to .gitignore
8a56e1dfd Delete resources/_gen/images
1521e79b6 Update introduction.md
c7110a597 Update introduction.md
f7c4d220f Update introduction.md
c14ecf1ff Update introduction.md
c4b388c20 Fix shortcode in hugo-pipes/introduction (#1605)
d9876bd68 Update introduction.md
a39a532d5 Update introduction.md
fa8758122 netlify: Hugo 0.90.1
50cc40570 Update Contribute to Hugo Development
fc98fc82c Add .vscode dir to .gitignore (#1602)
8f8bcdefc Add frontmatter param to code-toggle shortcode (#1601)
f3b432c7c Add markdownlint configuration file (#1597)
7ea3aea92 netlify: Bump to 0.90.0
e2b6f990b docs: Regenerate docs helper
c02259e33 Merge commit '8d9511a08f14260cbfb73119e4afae50e5a9966d'
2e3573899 Add custom font support to images.Text
3186e6458 images: Text filter that draws text with the given options (#9239)
424f219eb tpl/transform: Optional options for highlight func
7993f17d0 Implement XML data support
1eb9d34cf Make resources.Get use a file cache for remote resources
8a0fffd56 Add remote support to resources.Get

git-subtree-dir: docs
git-subtree-split: 4eb10c1a96
2021-12-13 21:04:12 +01:00
Joe Mooring a037be774d Improve handling of remote image/jpeg resources (#9278)
Add jpe, jif, and jfif to image/jpeg extensions.
For remote image/jpeg without extension, always use jpg extension.

Closes #9275
2021-12-13 08:55:15 +01:00
hitzhangjie 8a005538db Fix Dockerfile
Mage uses git, so we should install git before run mage.

Closes #9261
2021-12-12 14:45:08 +01:00
Joe Mooring 657d0272e3 Remove debug statement 2021-12-11 22:59:36 +01:00
Bjørn Erik Pedersen 159120cddd Fix deprecation notice
Asterisks needs to be quoted on command line.
2021-12-11 11:54:28 +01:00
hugoreleaser 3f0d49e502 releaser: Prepare repository for 0.91.0-DEV
[ci skip]
2021-12-10 11:42:18 +00:00
hugoreleaser 4890788974 releaser: Bump versions for release of 0.90.1
[ci skip]
2021-12-10 10:56:40 +00:00
hugoreleaser 3075eaa3b0 releaser: Add release notes for 0.90.1
[ci skip]
2021-12-10 10:56:40 +00:00
Bjørn Erik Pedersen 3bc6830411 Remove the retries on error in remote resources.Get
Fixes #9271
See  #9259
2021-12-10 11:10:41 +01:00
Bjørn Erik Pedersen e4d6ec94b5 Allow user to handle/ignore errors in resources.Get
In Hugo 0.90.0 we introduced remote support in `resources.Get`.

But with remote resources comes with a higher chance of failing a build (network issues, remote server down etc.).

Before this commit we always failed the build on any unexpected error.

This commit allows the user to check for any error (and potentially fall back to a default local resource):

```htmlbars
{{ $result := resources.Get "https://gohugo.io/img/hugo-logo.png" }}
{{ with $result }}
        {{ if .Err }}
        {{/* log the error, insert a default image etc. *}}
        {{ else }}
        <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
        {{ end }}
{{ end }}
```

Note that the default behaviour is still to fail the build, but we will delay that error until you start using the `Resource`.

Fixes #9529
2021-12-10 11:10:41 +01:00
Bjørn Erik Pedersen 6260455ba7 Make resource.Get return nil on 404 not found
This is in line with the interface declaration and also how local lookups work.

Fixes #9267
2021-12-10 11:10:41 +01:00
Bjørn Erik Pedersen c397975af8 Update to Go 1.17.5
Fixes #9269
2021-12-10 10:36:02 +01:00
Bjørn Erik Pedersen 965a6cbff9 Update to Go 1.17.4 and remove timeout in resources.Get
Fixes #9265
2021-12-09 13:33:30 +01:00
hugoreleaser 34a96290fa releaser: Prepare repository for 0.91.0-DEV
[ci skip]
2021-12-08 10:11:31 +00:00
hugoreleaser dd0d3fdbbe releaser: Bump versions for release of 0.90.0
[ci skip]
2021-12-08 09:33:46 +00:00
hugoreleaser b921752331 releaser: Add release notes for 0.90.0
[ci skip]
2021-12-08 09:33:46 +00:00
Bjørn Erik Pedersen 0fa40ce58f releaser: Simplify the release process
Make it into a one step even for major releases.
2021-12-08 09:38:15 +01:00
Bjørn Erik Pedersen bf537f1c6d releaser: Remove unused code 2021-12-08 09:02:45 +01:00
Bjørn Erik Pedersen e86b331138 docs: Regenerate docs helper 2021-12-08 08:56:16 +01:00
Bjørn Erik Pedersen 6c841a691e Merge commit '8d9511a08f14260cbfb73119e4afae50e5a9966d' 2021-12-08 08:54:25 +01:00
Bjørn Erik Pedersen 8d9511a08f Squashed 'docs/' changes from 39a7fac34..316cec249
316cec249 Update future events template example (#1595)
3bde7d489 Install mage outside module (#1592)
762e27eff Clarify ignoreFiles regex matching
4d0032051 Add id attribute to h2 elements (#1590)
8262b077c Improve inline resource examples (#1587)
2eae7c7ec fix disqus example name (#1588)
a772f4804 Added install instructions for openSUSE Tumbleweed (#1459)
7ad1c301b Remove screen capture from Hosting on GitHub page (#1586)
a58541f49 add more details on about gh-pages and baseURL on hosting-on-github.md (#1346)
3bd0b46dc Update configuration page (#1585)
4cf1f013e Update OS functions
2c45a95c2 Remove getting-started/code-toggle/
40fdff598 Describe artificial language private use subtags (#1577)
91011d210 Remove google_news from list of internal templates (#1576)
36c7879e4 Update the .Unix function
731063488 Remove a showcase
818c371a0 Update index.md
3136d39d9 netlify: Hugo 0.89.4
092bc9278 Merge branch 'tempv0.89.4'
18e01f105 releaser: Add release notes to /docs for release of 0.89.4
79135281f Correct and sort list of target image formats (#1574)
af4170c7e netlify: Hugo 0.89.3
7f5444251 Merge branch 'tempv0.89.3'
a32e4a6c2 releaser: Add release notes to /docs for release of 0.89.3
6dd3dc3f9 Update configuration.md
5fbe741d7 Update index.md (#1570)
37a69496f netlify: Bump to Hugo 0.89.2
3b293f1f4 Merge branch 'tempv0.89.2'
64c934e7a releaser: Add release notes to /docs for release of 0.89.2
919c51c7d Update index.md
13dd463b1 netlify: Hugo 0.89.1
d8cda1474 releaser: Add release notes to /docs for release of 0.89.1
a2adf7742 releaser: Add release notes to /docs for release of 0.89.1
c3088c4fc Add code toggle to menus page (#1568)
2d0f38978 Remove blank lines from code-toggle output (#1564)
7cf058bfd Add localization examples (#1563)
cf8627c2e Fixing typos, fixing incomplete link (#1561)
c78cc014b Document the removePathAccents setting
70beddaf4 Make corrections to 0.89.0 release notes (#1560)
1917195f0 Update index.md
7fb8e070c Run hugo --gc
1772d45fb Release 0.89.0
d9006179b Merge branch 'tempv0.89.0'
8db86b61e releaser: Add release notes to /docs for release of 0.89.0
abf268571 docs: Regen CLI docs
fbbdb0ab1 Update the timeout default
9cbd1c15a Fix description of lang.FormatNumberCustom
6043b54cc Remove "render" keyword from Host on Render page
f8ea8e84f Clarify description of front matter url (#1557)
91a0c9954 Update Twitter shortcode oEmbed endpoint
79a7405b8 Merge commit 'aa5ac36a3eb68b86c803caec703869efefc8447e'
57667bae6 hugofs: Add includeFiles and excludeFiles to mount configuration
0c9ee0a04 Allow multiple plugins in the PostCSS options map
155799e6b docs: Create path.Clean documentation

git-subtree-dir: docs
git-subtree-split: 316cec2494
2021-12-08 08:42:31 +01:00
Bjørn Erik Pedersen e71d715b9b Add custom font support to images.Text
Fixes #9253
2021-12-07 16:53:02 +01:00
Bjørn Erik Pedersen e61cdf335f images: Fix cache busting of image text filter
See #9238
2021-12-07 16:53:02 +01:00
dependabot[bot] 6c3bc5ebad build(deps): bump github.com/getkin/kin-openapi from 0.80.0 to 0.85.0
Bumps [github.com/getkin/kin-openapi](https://github.com/getkin/kin-openapi) from 0.80.0 to 0.85.0.
- [Release notes](https://github.com/getkin/kin-openapi/releases)
- [Commits](https://github.com/getkin/kin-openapi/compare/v0.80.0...v0.85.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>
2021-12-07 13:12:42 +01:00
Paul van Brouwershaven 283394a4fd images: Text filter that draws text with the given options (#9239)
Fixes #9238
2021-12-07 11:29:55 +01:00
Joe Mooring 5538507e90 tpl/transform: Optional options for highlight func
Closes #9249
Fixes gohugoio/hugoDocs#63
2021-12-07 11:26:56 +01:00
Bjørn Erik Pedersen b4f27ef8e7 deps: Upgrade github.com/evanw/esbuild v0.13.12 => v0.14.2 (note)
Note: ESBuild v0.14.0 contains backwards-incompatible changes, see https://github.com/evanw/esbuild/releases/tag/v0.14.0

Fixes #9244
2021-12-04 16:28:50 +01:00
Bjørn Erik Pedersen 3473e31eb1 releaser: Add "note" to Note regexp 2021-12-04 16:27:56 +01:00
dependabot[bot] fa0da004a9 build(deps): bump github.com/mitchellh/mapstructure from 1.4.2 to 1.4.3
Bumps [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) from 1.4.2 to 1.4.3.
- [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.2...v1.4.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-03 18:16:50 +01:00
Bjørn Erik Pedersen 24a893cf83 releaser: Rework and simplify to use GitHub only for release notes 2021-12-03 17:02:20 +01:00
dependabot[bot] bf1564bd2c build(deps): bump google.golang.org/api from 0.51.0 to 0.61.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.51.0 to 0.61.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.51.0...v0.61.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>
2021-12-03 17:01:37 +01:00
Paul van Brouwershaven cd44d409ba media: Add rss suffix for application/rss+xml
Closes #9233
2021-12-03 12:45:49 +01:00
Bjørn Erik Pedersen 9a326d5636 parser: Add a test case in format resolution
See #9233
2021-12-03 10:34:34 +01:00
Bjørn Erik Pedersen b10381fbe0 lazy: Reset error in Reset
To prevent sticky errors on server rebuilds.

Fixes #7043
Closes #9194
2021-12-02 18:06:19 +01:00
Paul van Brouwershaven 0eaaa8fee3 Implement XML data support
Example:

```
{{ with resources.Get "https://example.com/rss.xml" | transform.Unmarshal }}
    {{ range .channel.item }}
        <strong>{{ .title | plainify | htmlUnescape }}</strong><br />
        <p>{{ .description | plainify | htmlUnescape }}</p>
        {{ $link := .link | plainify | htmlUnescape }}
        <a href="{{ $link }}">{{ $link }}</a><br />
        <hr>
    {{ end }}
{{ end }}
```

Closes #4470
2021-12-02 17:30:36 +01:00
Joe Mooring 58adbeef88 Validate private use language tags
Fixes #9119
2021-12-02 16:30:53 +01:00
Bjørn Erik Pedersen 93572e5318 resources: Add timeout to the HTTP request in Get
Workaround for https://github.com/golang/go/issues/49366
2021-12-02 16:11:14 +01:00
Bjørn Erik Pedersen 94f149b21e Add a remote retry for resources.Get 2021-12-02 16:11:14 +01:00
Paul van Brouwershaven 66753416b5 Make resources.Get use a file cache for remote resources
Closes #9228
2021-12-02 12:56:25 +01:00
Joe Mooring 133e4bfbee Remove empty href element from pagination template
Closes #9149
2021-12-02 09:14:30 +01:00
Joe Mooring f122771fb1 Check for empty deployment targets and matchers
Fixes #9220
2021-12-01 10:17:42 +01:00
Bjørn Erik Pedersen 08a863e1e8 resources: Adjust the remote Get cache so it does not get evicted on restarts
We fill improve this, soon.
2021-11-30 16:54:01 +01:00
Paul van Brouwershaven 8aa7257f65 Add remote support to resources.Get
Closes #5255
Supports #9044
2021-11-30 11:49:51 +01:00
Joe Mooring 75a823a36a Add deprecation warning to google_news template
Closes #9172
2021-11-21 11:25:17 +01:00
Bjørn Erik Pedersen 5e0947c5b5 helpers: Make UniqueStringsReuse allocation free
This should be faster for all of our use cases (small string slices), but more important, it makes UniqueStringsReuse allocation free.

```bash
name                                 old time/op    new time/op    delta
UniqueStrings/Safe-16                   776ns ± 0%     305ns ± 0%   -60.68%  (p=0.029 n=4+4)
UniqueStrings/Reuse_slice-16            621ns ± 3%     174ns ± 0%   -72.01%  (p=0.029 n=4+4)
UniqueStrings/Reuse_slice_sorted-16     319ns ± 4%     313ns ± 0%    -2.01%  (p=0.029 n=4+4)

name                                 old alloc/op   new alloc/op   delta
UniqueStrings/Safe-16                    224B ± 0%      128B ± 0%   -42.86%  (p=0.029 n=4+4)
UniqueStrings/Reuse_slice-16            96.0B ± 0%      0.0B       -100.00%  (p=0.029 n=4+4)
UniqueStrings/Reuse_slice_sorted-16     24.0B ± 0%     24.0B ± 0%      ~     (all equal)

name                                 old allocs/op  new allocs/op  delta
UniqueStrings/Safe-16                    7.00 ± 0%      1.00 ± 0%   -85.71%  (p=0.029 n=4+4)
UniqueStrings/Reuse_slice-16             6.00 ± 0%      0.00       -100.00%  (p=0.029 n=4+4)
UniqueStrings/Reuse_slice_sorted-16      1.00 ± 0%      1.00 ± 0%      ~     (all equal)
```
2021-11-17 10:44:03 +01:00
hugoreleaser 0b70b46aad releaser: Prepare repository for 0.90.0-DEV
[ci skip]
2021-11-17 09:05:38 +00:00
hugoreleaser ab01ba6e7e releaser: Add release notes to /docs for release of 0.89.4
[ci skip]
2021-11-17 08:24:08 +00:00
hugoreleaser cc08c09592 releaser: Bump versions for release of 0.89.4
[ci skip]
2021-11-17 08:24:08 +00:00
hugoreleaser f97da9eca9 releaser: Add release notes for 0.89.4
[ci skip]
2021-11-17 08:24:08 +00:00
Bjørn Erik Pedersen 2e70f61fb0 Fix content dir resolution when main project is a Hugo Module
Fixes #9177
2021-11-16 20:42:34 +01:00
hugoreleaser 1ed8069a3a releaser: Prepare repository for 0.90.0-DEV
[ci skip]
2021-11-15 12:56:41 +00:00
hugoreleaser c88cdb5610 releaser: Add release notes to /docs for release of 0.89.3
[ci skip]
2021-11-15 12:17:45 +00:00
hugoreleaser e1064d2195 releaser: Bump versions for release of 0.89.3
[ci skip]
2021-11-15 12:17:45 +00:00
hugoreleaser bf489b96d3 releaser: Add release notes for 0.89.3
[ci skip]
2021-11-15 12:17:45 +00:00
Bjørn Erik Pedersen b8155452ac Improve error when we cannot determine content directory in "hugo new"
See #9166
2021-11-15 11:21:39 +01:00
Joe Mooring 08552a7a4c deps: Upgrade github.com/yuin/goldmark v1.4.3 => v1.4.4
Fixes #9159
2021-11-15 09:43:24 +01:00
Bjørn Erik Pedersen fdad91fd96 commands: Make sure pollInterval is always set
Fixes #9165
2021-11-13 21:45:51 +01:00
Bjørn Erik Pedersen 5f3f60898c create: Improve archetype directory discovery and tests
Updates #9146
2021-11-12 12:05:31 +01:00
Bjørn Erik Pedersen 057d02de25 create: Add a log statement when archetype is a directory
See #9157
2021-11-12 12:05:31 +01:00
Bjørn Erik Pedersen 43ac59da85 create: Always print "Content ... created"
This indeliberately changed to INFO level in Hugo 0.89.0.

Fixes #9157
2021-11-12 12:05:31 +01:00
Bjørn Erik Pedersen ab5c6990a5 commands: Fix missing file locking in server partial render
Fixes #9162
2021-11-12 09:29:04 +01:00
Patrick Kollitsch 9369d13e59 modules: Improve error message 2021-11-10 09:32:17 +01:00
hugoreleaser 805c24c32c releaser: Prepare repository for 0.90.0-DEV
[ci skip]
2021-11-08 15:56:14 +00:00
1095 changed files with 24721 additions and 21694 deletions
+1 -2
View File
@@ -1,6 +1,6 @@
defaults: &defaults
docker:
- image: bepsays/ci-goreleaser:1.17.2
- image: bepsays/ci-goreleaser:1.1800.300
environment:
CGO_ENABLED: "0"
@@ -18,7 +18,6 @@ jobs:
go mod download
sleep 5
go mod verify
go test -p 1 ./...
- persist_to_workspace:
root: .
paths: .
+1 -1
View File
@@ -1,6 +1,6 @@
---
name: 'Bug report'
labels: 'bug'
labels: 'Bug, NeedsTriage'
assignees: ''
about: Create a report to help us improve
---
+5 -2
View File
@@ -1,8 +1,11 @@
---
name: Proposal
about: Suggest an idea for Hugo
about: Propose a new feature for Hugo
title: ''
labels: 'Proposal'
labels: 'Proposal, NeedsTriage'
assignees: ''
---
<!-- Describe this new feature. Think about if it really belongs in the Hugo core module; you may want to discuss it on https://discourse.gohugo.io/ first. -->
-23
View File
@@ -1,23 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 120
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 30
# Issues with these labels will never be considered stale
exemptLabels:
- Keep
- Security
# Label to use when marking an issue as stale
staleLabel: Stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
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.
If this is a **bug** and you can still reproduce this error on the <code>master</code> branch, please reply with all of the information you have about it in order to keep the issue open.
If this is a **feature request**, and you feel that it is still relevant and valuable, please tell us why.
This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+47
View File
@@ -0,0 +1,47 @@
name: 'Close stale and lock closed issues and PRs'
on:
workflow_dispatch:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@08e671be8ac8944d0e132aa71d0ae8ccfb347675
with:
issue-inactive-days: 21
add-issue-labels: 'Outdated'
issue-comment: >
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
pr-comment: >
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
- uses: actions/stale@04a1828bc18ada028d85a0252a47cd2963a91abe
with:
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.
If this is a **bug** and you can still reproduce this error on the <code>master</code> branch, please reply with all of the information you have about it in order to keep the issue open.
If this is a **feature request**, and you feel that it is still relevant and valuable, please tell us why.
This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.
stale-pr-message: This PR 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.
Please check https://github.com/gohugoio/hugo/blob/master/CONTRIBUTING.md#code-contribution and verify that this code contribution fits with the description. If yes, tell is in a comment.
This PR will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.
stale-issue-label: 'Stale'
exempt-issue-labels: 'Keep,Security'
stale-pr-label: 'Stale'
exempt-pr-labels: 'Keep,Security'
+12 -4
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]
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
@@ -44,6 +50,7 @@ jobs:
- if: matrix.os == 'windows-latest'
run: |
choco install pandoc
choco install mingw --version 10.2.0 --allow-downgrade
- run: pandoc -v
- if: matrix.os == 'ubuntu-latest'
name: Install dart-sass-embedded Linux
@@ -66,10 +73,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
+2
View File
@@ -1,6 +1,7 @@
/hugo
docs/public*
/.idea
.vscode/*
hugo.exe
*.test
*.prof
@@ -25,3 +26,4 @@ resources/sunset.jpg
vendor
.hugo_build.lock
+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:
+3 -3
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 .
@@ -19,8 +19,8 @@ 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 && \
go get github.com/magefile/mage
apk add --no-cache gcc g++ musl-dev git && \
go install github.com/magefile/mage
RUN mage hugo && mage install
+78 -54
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][].
[Website](https://gohugo.io) |
[Forum](https://discourse.gohugo.io) |
@@ -54,11 +54,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
@@ -85,6 +85,8 @@ git clone git@github.com:gohugoio/hugo.git
```
## 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.
For a complete guide to contributing to Hugo, see the [Contribution Guide](CONTRIBUTING.md).
We welcome contributions to Hugo of any kind including documentation, themes,
@@ -128,97 +130,119 @@ 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{}
},
}
+15 -2
View File
@@ -175,7 +175,12 @@ func (c *Cache) GetOrCreate(id string, create func() (io.ReadCloser, error)) (It
return info, r, nil
}
r, err := create()
var (
r io.ReadCloser
err error
)
r, err = create()
if err != nil {
return info, nil, err
}
@@ -206,7 +211,12 @@ func (c *Cache) GetOrCreateBytes(id string, create func() ([]byte, error)) (Item
return info, b, err
}
b, err := create()
var (
b []byte
err error
)
b, err = create()
if err != nil {
return info, nil, err
}
@@ -285,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
}
+25 -12
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"
)
@@ -34,19 +36,21 @@ const (
cachesConfigKey = "caches"
resourcesGenDir = ":resourceDir/_gen"
cacheDirProject = ":cacheDir/:project"
)
var defaultCacheConfig = Config{
MaxAge: -1, // Never expire
Dir: ":cacheDir/:project",
Dir: cacheDirProject,
}
const (
cacheKeyGetJSON = "getjson"
cacheKeyGetCSV = "getcsv"
cacheKeyImages = "images"
cacheKeyAssets = "assets"
cacheKeyModules = "modules"
cacheKeyGetJSON = "getjson"
cacheKeyGetCSV = "getcsv"
cacheKeyImages = "images"
cacheKeyAssets = "assets"
cacheKeyModules = "modules"
cacheKeyGetResource = "getresource"
)
type Configs map[string]Config
@@ -70,6 +74,10 @@ var defaultCacheConfigs = Configs{
MaxAge: -1,
Dir: resourcesGenDir,
},
cacheKeyGetResource: Config{
MaxAge: -1, // Never expire
Dir: cacheDirProject,
},
}
type Config struct {
@@ -111,6 +119,11 @@ func (f Caches) AssetsCache() *Cache {
return f[cacheKeyAssets]
}
// GetResourceCache gets the file cache for remote resources.
func (f Caches) GetResourceCache() *Cache {
return f[cacheKeyGetResource]
}
func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
c := make(Configs)
valid := make(map[string]bool)
@@ -142,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 == "" {
@@ -151,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
@@ -186,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)
}
}
@@ -231,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)
}
+12 -6
View File
@@ -50,7 +50,8 @@ maxAge = "11h"
dir = "/path/to/c2"
[caches.images]
dir = "/path/to/c3"
[caches.getResource]
dir = "/path/to/c4"
`
cfg, err := config.FromConfigString(configStr, "toml")
@@ -59,7 +60,7 @@ dir = "/path/to/c3"
decoded, err := DecodeConfig(fs, cfg)
c.Assert(err, qt.IsNil)
c.Assert(len(decoded), qt.Equals, 5)
c.Assert(len(decoded), qt.Equals, 6)
c2 := decoded["getcsv"]
c.Assert(c2.MaxAge.String(), qt.Equals, "11h0m0s")
@@ -68,6 +69,10 @@ dir = "/path/to/c3"
c3 := decoded["images"]
c.Assert(c3.MaxAge, qt.Equals, time.Duration(-1))
c.Assert(c3.Dir, qt.Equals, filepath.FromSlash("/path/to/c3/filecache/images"))
c4 := decoded["getresource"]
c.Assert(c4.MaxAge, qt.Equals, time.Duration(-1))
c.Assert(c4.Dir, qt.Equals, filepath.FromSlash("/path/to/c4/filecache/getresource"))
}
func TestDecodeConfigIgnoreCache(t *testing.T) {
@@ -94,7 +99,8 @@ maxAge = 3456
dir = "/path/to/c2"
[caches.images]
dir = "/path/to/c3"
[caches.getResource]
dir = "/path/to/c4"
`
cfg, err := config.FromConfigString(configStr, "toml")
@@ -103,7 +109,7 @@ dir = "/path/to/c3"
decoded, err := DecodeConfig(fs, cfg)
c.Assert(err, qt.IsNil)
c.Assert(len(decoded), qt.Equals, 5)
c.Assert(len(decoded), qt.Equals, 6)
for _, v := range decoded {
c.Assert(v.MaxAge, qt.Equals, time.Duration(0))
@@ -129,7 +135,7 @@ func TestDecodeConfigDefault(t *testing.T) {
c.Assert(err, qt.IsNil)
c.Assert(len(decoded), qt.Equals, 5)
c.Assert(len(decoded), qt.Equals, 6)
imgConfig := decoded[cacheKeyImages]
jsonConfig := decoded[cacheKeyGetJSON]
@@ -178,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
+121 -37
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"
@@ -61,6 +67,8 @@ type commandeer struct {
logger loggers.Logger
serverConfig *config.Server
buildLock func() (unlock func(), err error)
// Loading state
mustHaveConfigFile bool
failOnInitErr bool
@@ -70,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
@@ -86,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
@@ -103,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{}),
@@ -114,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")
}
@@ -152,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 {
@@ -335,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
@@ -366,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 {
@@ -394,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.
@@ -418,7 +497,12 @@ func (c *commandeer) loadConfig() error {
if h == nil || c.failOnInitErr {
err = createErr
}
c.hugoSites = h
// TODO(bep) improve.
if c.buildLock == nil && h != nil {
c.buildLock = h.LockBuild
}
close(c.created)
})
+16 -16
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"
@@ -48,7 +47,6 @@ func (b *commandsBuilder) addAll() *commandsBuilder {
newVersionCmd(),
newEnvCmd(),
b.newConfigCmd(),
newCheckCmd(),
b.newDeployCmd(),
b.newConvertCmd(),
b.newNewCmd(),
@@ -131,8 +129,7 @@ type hugoCmd struct {
var _ cmder = (*nilCommand)(nil)
type nilCommand struct {
}
type nilCommand struct{}
func (c *nilCommand) getCommand() *cobra.Command {
return nil
@@ -152,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 {
@@ -213,6 +210,7 @@ type hugoBuilderCommon struct {
buildWatch bool
poll string
clock string
gc bool
@@ -245,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 {
@@ -281,8 +279,8 @@ func (cc *hugoBuilderCommon) handleCommonBuilderFlags(cmd *cobra.Command) {
cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
cmd.PersistentFlags().StringVarP(&cc.environment, "environment", "e", "", "build environment")
cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory")
cmd.PersistentFlags().BoolP("ignoreVendor", "", false, "ignores any _vendor 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) {
@@ -297,21 +295,23 @@ 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")
cmd.Flags().Bool("templateMetrics", false, "display metrics about template executions")
cmd.Flags().Bool("templateMetricsHints", false, "calculate some improvement hints when combined with --templateMetrics")
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)")
+71 -85
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,15 +36,13 @@ 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
@@ -56,8 +52,7 @@ func TestExecute(t *testing.T) {
})
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)
@@ -162,22 +151,12 @@ func TestFlags(t *testing.T) {
args []string
check func(c *qt.C, cmd *serverCmd)
}{
{
// https://github.com/gohugoio/hugo/issues/7642
name: "ignoreVendor as bool",
args: []string{"server", "--ignoreVendor"},
check: func(c *qt.C, cmd *serverCmd) {
cfg := config.New()
cmd.flagsToConfig(cfg)
c.Assert(cfg.Get("ignoreVendor"), qt.Equals, true)
},
},
{
// https://github.com/gohugoio/hugo/issues/7642
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/**")
},
@@ -200,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))
@@ -217,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")
@@ -230,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)
},
},
@@ -262,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)
@@ -284,11 +258,14 @@ func TestCommandsExecute(t *testing.T) {
// no args = hugo build
{nil, []string{sourceFlag}, ""},
{nil, []string{sourceFlag, "--renderToMemory"}, ""},
{[]string{"completion", "bash"}, nil, ""},
{[]string{"completion", "fish"}, nil, ""},
{[]string{"completion", "powershell"}, nil, ""},
{[]string{"completion", "zsh"}, nil, ""},
{[]string{"config"}, []string{sourceFlag}, ""},
{[]string{"convert", "toTOML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "toml")}, ""},
{[]string{"convert", "toYAML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "yaml")}, ""},
{[]string{"convert", "toJSON"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "json")}, ""},
{[]string{"gen", "autocomplete"}, []string{"--completionfile=" + filepath.Join(dirOut, "autocomplete.txt")}, ""},
{[]string{"gen", "chromastyles"}, []string{"--style=manni"}, ""},
{[]string{"gen", "doc"}, []string{"--dir=" + filepath.Join(dirOut, "doc")}, ""},
{[]string{"gen", "man"}, []string{"--dir=" + filepath.Join(dirOut, "man")}, ""},
@@ -303,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)
}
})
}
}
@@ -335,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 := `
@@ -358,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
@@ -382,28 +364,32 @@ Content
`)
writeFile(t, filepath.Join(d, "layouts", "_default", "single.html"), `
writeFile(t, filepath.Join(dir, "layouts", "_default", "single.html"), `
Single: {{ .Title }}
`)
writeFile(t, filepath.Join(d, "layouts", "_default", "list.html"), `
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"`
}{
+5 -7
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"
@@ -142,7 +140,7 @@ func (cc *convertCmd) convertAndSavePage(p page.Page, site *hugolib.Site, target
return nil
}
errMsg := fmt.Errorf("Error processing file %q", p.Path())
errMsg := fmt.Errorf("Error processing file %q", p.File().Path())
site.Log.Infoln("Attempting to convert", p.File().Filename())
@@ -185,15 +183,15 @@ func (cc *convertCmd) convertAndSavePage(p page.Page, site *hugolib.Site, target
newFilename := p.File().Filename()
if cc.outputDir != "" {
contentDir := strings.TrimSuffix(newFilename, p.Path())
contentDir := strings.TrimSuffix(newFilename, p.File().Path())
contentDir = filepath.Base(contentDir)
newFilename = filepath.Join(cc.outputDir, contentDir, p.Path())
newFilename = filepath.Join(cc.outputDir, contentDir, p.File().Path())
}
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
-1
View File
@@ -31,7 +31,6 @@ func newGenCmd() *genCmd {
})
cc.cmd.AddCommand(
newGenautocompleteCmd().getCommand(),
newGenDocCmd().getCommand(),
newGenManCmd().getCommand(),
createGenDocsHelper().getCommand(),
-93
View File
@@ -1,93 +0,0 @@
// Copyright 2015 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 commands
import (
"io"
"os"
"github.com/spf13/cobra"
jww "github.com/spf13/jwalterweatherman"
)
var _ cmder = (*genautocompleteCmd)(nil)
type genautocompleteCmd struct {
autocompleteTarget string
// bash, zsh, fish or powershell
autocompleteType string
*baseCmd
}
func newGenautocompleteCmd() *genautocompleteCmd {
cc := &genautocompleteCmd{}
cc.baseCmd = newBaseCmd(&cobra.Command{
Use: "autocomplete",
Short: "Generate shell autocompletion script for Hugo",
Long: `Generates a shell autocompletion script for Hugo.
The script is written to the console (stdout).
To write to file, add the ` + "`--completionfile=/path/to/file`" + ` flag.
Add ` + "`--type={bash, zsh, fish or powershell}`" + ` flag to set alternative
shell type.
Logout and in again to reload the completion scripts,
or just source them in directly:
$ . /etc/bash_completion or /path/to/file`,
RunE: func(cmd *cobra.Command, args []string) error {
var err error
var target io.Writer
if cc.autocompleteTarget == "" {
target = os.Stdout
} else {
target, _ = os.OpenFile(cc.autocompleteTarget, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
}
switch cc.autocompleteType {
case "bash":
err = cmd.Root().GenBashCompletion(target)
case "zsh":
err = cmd.Root().GenZshCompletion(target)
case "fish":
err = cmd.Root().GenFishCompletion(target, true)
case "powershell":
err = cmd.Root().GenPowerShellCompletion(target)
default:
return newUserError("Unsupported completion type")
}
if err != nil {
return err
}
if cc.autocompleteTarget != "" {
jww.FEEDBACK.Println(cc.autocompleteType+" completion file for Hugo saved to", cc.autocompleteTarget)
}
return nil
},
})
cc.cmd.PersistentFlags().StringVarP(&cc.autocompleteTarget, "completionfile", "f", "", "autocompletion file, defaults to stdout")
cc.cmd.PersistentFlags().StringVarP(&cc.autocompleteType, "type", "t", "bash", "autocompletion type (bash, zsh, fish, or powershell)")
return cc
}
+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.`,
+4 -4
View File
@@ -53,22 +53,22 @@ 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}
}
// Catch some of the obvious user errors from Cobra.
// We don't want to show the usage message for every error.
// The below may be to generic. Time will show.
var userErrorRegexp = regexp.MustCompile("argument|flag|shorthand")
var userErrorRegexp = regexp.MustCompile("unknown flag")
func isUserError(err error) bool {
if cErr, ok := err.(commandError); ok && cErr.isUserError() {
+58 -38
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,7 +206,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
"forceSyncStatic",
"noTimes",
"noChmod",
"ignoreVendor",
"noBuildLock",
"ignoreVendorPaths",
"templateMetrics",
"templateMetricsHints",
@@ -217,7 +224,8 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
"dryRun",
"force",
"gc",
"i18n-warnings",
"printI18nWarnings",
"printUnusedTemplates",
"invalidateCDN",
"layoutDir",
"logFile",
@@ -244,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) {
@@ -276,12 +284,7 @@ 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
langCount map[string]uint64
@@ -290,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)
}()
@@ -300,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
}
@@ -354,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()
@@ -388,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() {
@@ -503,18 +506,22 @@ 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 {
if createCounter, ok := c.publishDirFs.(hugofs.DuplicatesReporter); ok {
dupes := createCounter.ReportDuplicates()
if dupes != "" {
c.logger.Warnln("Duplicate target paths:", dupes)
}
}
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())
}
}
if c.h.buildWatch {
@@ -542,7 +549,6 @@ func (c *commandeer) build() error {
}
func (c *commandeer) serverBuild() error {
stopProfiling, err := c.initProfiling()
if err != nil {
return err
@@ -631,11 +637,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)
@@ -648,7 +650,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")
@@ -680,6 +685,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()))
}
@@ -730,16 +739,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 {
@@ -765,7 +774,9 @@ func (c *commandeer) partialReRender(urls ...string) error {
for _, url := range urls {
visited[url] = true
}
return c.hugo().Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.wasError})
// Note: We do not set NoBuildLock as the file lock is not acquired at this stage.
return c.hugo().Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.wasError})
}
func (c *commandeer) fullRebuild(changeType string) {
@@ -843,13 +854,22 @@ func (c *commandeer) newWatcher(pollIntervalStr string, dirList ...string) (*wat
c.logger.Printf("Use watcher with poll interval %v", pollInterval)
}
if pollInterval == 0 {
pollInterval = 500 * time.Millisecond
}
watcher, err := watcher.New(500*time.Millisecond, pollInterval, poll)
if err != nil {
return nil, err
}
spec := c.hugo().Deps.SourceSpec
for _, d := range dirList {
if d != "" {
if spec.IgnoreFile(d) {
continue
}
_ = watcher.Add(d)
}
}
@@ -867,7 +887,7 @@ func (c *commandeer) newWatcher(pollIntervalStr string, dirList ...string) (*wat
for {
select {
case evs := <-watcher.Events:
unlock, err := c.hugo().BaseFs.LockBuild()
unlock, err := c.buildLock()
if err != nil {
c.logger.Errorln("Failed to acquire a build lock: %s", err)
return
@@ -898,7 +918,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"})
+1 -1
View File
@@ -96,7 +96,7 @@ func (b *commandsBuilder) newModCmd() *modCmd {
const commonUsage = `
Note that Hugo will always start out by resolving the components defined in the site
configuration, provided by a _vendor directory (if no --ignoreVendor flag provided),
configuration, provided by a _vendor directory (if no --ignoreVendorPaths flag provided),
Go Modules, or a folder inside the themes directory, in that order.
See https://gohugo.io/hugo-modules/ for more information.
+7 -6
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"
@@ -52,7 +51,7 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
RunE: cc.newSite,
}
cmd.Flags().StringVarP(&cc.configFormat, "format", "f", "toml", "config & frontmatter format")
cmd.Flags().StringVarP(&cc.configFormat, "format", "f", "toml", "config file format")
cmd.Flags().Bool("force", false, "init inside non-empty directory")
cc.baseBuilderCmd = b.newBuilderBasicCmd(cmd)
@@ -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
+1
View File
@@ -1,3 +1,4 @@
//go:build release
// +build release
// Copyright 2017-present The Hugo Authors. All rights reserved.
+1
View File
@@ -1,3 +1,4 @@
//go:build !release
// +build !release
// Copyright 2018 The Hugo Authors. All rights reserved.
+184 -76
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 {
@@ -453,15 +472,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 +528,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 +571,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 +593,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 +694,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 +723,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)}
+175 -28
View File
@@ -17,6 +17,7 @@ import (
"fmt"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
@@ -24,22 +25,142 @@ import (
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
"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)
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
config := `
[markup]
[markup.highlight]
linenos='table'
`
r := runServerTest(c, 0, config)
c.Assert(r.err, qt.IsNotNil)
c.Assert(r.err.Error(), qt.Contains, "cannot parse 'Highlight.LineNos' as bool:")
}
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, "=")
}
r := runServerTest(c, 1, config, args...)
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)
}{
// 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, "=")
}
r := runServerTest(c, test.numservers, test.config, args...)
test.assert(c, r)
})
}
}
type serverTestResult struct {
err error
homesContent []string
publicDirnames map[string]bool
}
func runServerTest(c *qt.C, getNumHomes int, config string, args ...string) (result serverTestResult) {
dir := createSimpleTestSite(c, testSiteConfig{configTOML: config})
sp, err := helpers.FindAvailablePort()
c.Assert(err, qt.IsNil)
port := sp.Port
defer func() {
os.RemoveAll(dir)
@@ -51,28 +172,54 @@ 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)}, 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/")
c.Assert(err, qt.IsNil)
defer resp.Body.Close()
homeContent := helpers.ReaderToString(resp.Body)
if getNumHomes > 0 {
// Esp. on slow CI machines, we need to wait a little before the web
// server is ready.
time.Sleep(567 * time.Millisecond)
result.homesContent = make([]string, getNumHomes)
for i := 0; i < getNumHomes; i++ {
func() {
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", port+i))
c.Check(err, qt.IsNil)
c.Check(resp.StatusCode, qt.Equals, http.StatusOK)
if err == nil {
defer resp.Body.Close()
result.homesContent[i] = helpers.ReaderToString(resp.Body)
}
}()
}
}
c.Assert(homeContent, qt.Contains, "List: Hugo Commands")
c.Assert(homeContent, qt.Contains, "Environment: development")
time.Sleep(1 * time.Second)
select {
case <-stop:
case stop <- true:
}
pubFiles, err := os.ReadDir(filepath.Join(dir, "public"))
c.Check(err, qt.IsNil)
result.publicDirnames = make(map[string]bool)
for _, f := range pubFiles {
result.publicDirnames[f.Name()] = true
}
result.err = wg.Wait()
return
// Stop the server.
stop <- true
}
func TestFixURL(t *testing.T) {
@@ -101,7 +248,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)
}
+12 -2
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
}
@@ -64,3 +64,13 @@ func Slice(args ...interface{}) interface{} {
}
return slice.Interface()
}
// StringSliceToInterfaceSlice converts ss to []interface{}.
func StringSliceToInterfaceSlice(ss []string) []any {
result := make([]any, len(ss))
for i, s := range ss {
result[i] = s
}
return result
}
+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)
}
+8 -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")
@@ -88,3 +62,10 @@ func GetGID() uint64 {
// We will, at least to begin with, make some Hugo features (SCSS with libsass) optional,
// and this error is used to signal those situations.
var ErrFeatureNotAvailable = errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")
// Must panics if err != nil.
func Must(err error) {
if err != nil {
panic(err)
}
}
+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
}
}
+276
View File
@@ -0,0 +1,276 @@
// Copyright 2020 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 hexec
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"regexp"
"strings"
"os"
"os/exec"
"github.com/cli/safeexec"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/config/security"
)
var WithDir = func(dir string) func(c *commandeer) {
return func(c *commandeer) {
c.dir = dir
}
}
var WithContext = func(ctx context.Context) func(c *commandeer) {
return func(c *commandeer) {
c.ctx = ctx
}
}
var WithStdout = func(w io.Writer) func(c *commandeer) {
return func(c *commandeer) {
c.stdout = w
}
}
var WithStderr = func(w io.Writer) func(c *commandeer) {
return func(c *commandeer) {
c.stderr = w
}
}
var WithStdin = func(r io.Reader) func(c *commandeer) {
return func(c *commandeer) {
c.stdin = r
}
}
var WithEnviron = func(env []string) func(c *commandeer) {
return func(c *commandeer) {
setOrAppend := func(s string) {
k1, _ := config.SplitEnvVar(s)
var found bool
for i, v := range c.env {
k2, _ := config.SplitEnvVar(v)
if k1 == k2 {
found = true
c.env[i] = s
}
}
if !found {
c.env = append(c.env, s)
}
}
for _, s := range env {
setOrAppend(s)
}
}
}
// New creates a new Exec using the provided security config.
func New(cfg security.Config) *Exec {
var baseEnviron []string
for _, v := range os.Environ() {
k, _ := config.SplitEnvVar(v)
if cfg.Exec.OsEnv.Accept(k) {
baseEnviron = append(baseEnviron, v)
}
}
return &Exec{
sc: cfg,
baseEnviron: baseEnviron,
}
}
// IsNotFound reports whether this is an error about a binary not found.
func IsNotFound(err error) bool {
var notFoundErr *NotFoundError
return errors.As(err, &notFoundErr)
}
// SafeCommand is a wrapper around os/exec Command which uses a LookPath
// implementation that does not search in current directory before looking in PATH.
// See https://github.com/cli/safeexec and the linked issues.
func SafeCommand(name string, arg ...string) (*exec.Cmd, error) {
bin, err := safeexec.LookPath(name)
if err != nil {
return nil, err
}
return exec.Command(bin, arg...), nil
}
// Exec encorces a security policy for commands run via os/exec.
type Exec struct {
sc security.Config
// os.Environ filtered by the Exec.OsEnviron whitelist filter.
baseEnviron []string
}
// 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 ...any) (Runner, error) {
if err := e.sc.CheckAllowedExec(name); err != nil {
return nil, err
}
env := make([]string, len(e.baseEnviron))
copy(env, e.baseEnviron)
cm := &commandeer{
name: name,
env: env,
}
return cm.command(arg...)
}
// Npx is a convenience method to create a Runner running npx --no-install <name> <args.
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...)
}
// Sec returns the security policies this Exec is configured with.
func (e *Exec) Sec() security.Config {
return e.sc
}
type NotFoundError struct {
name string
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("binary with name %q not found", e.name)
}
// Runner wraps a *os.Cmd.
type Runner interface {
Run() error
StdinPipe() (io.WriteCloser, error)
}
type cmdWrapper struct {
name string
c *exec.Cmd
outerr *bytes.Buffer
}
var notFoundRe = regexp.MustCompile(`(?s)not found:|could not determine executable`)
func (c *cmdWrapper) Run() error {
err := c.c.Run()
if err == nil {
return nil
}
if notFoundRe.MatchString(c.outerr.String()) {
return &NotFoundError{name: c.name}
}
return fmt.Errorf("failed to execute binary %q with args %v: %s", c.name, c.c.Args[1:], c.outerr.String())
}
func (c *cmdWrapper) StdinPipe() (io.WriteCloser, error) {
return c.c.StdinPipe()
}
type commandeer struct {
stdout io.Writer
stderr io.Writer
stdin io.Reader
dir string
ctx context.Context
name string
env []string
}
func (c *commandeer) command(arg ...any) (*cmdWrapper, error) {
if c == nil {
return nil, nil
}
var args []string
for _, a := range arg {
switch v := a.(type) {
case string:
args = append(args, v)
case func(*commandeer):
v(c)
default:
return nil, fmt.Errorf("invalid argument to command: %T", a)
}
}
bin, err := safeexec.LookPath(c.name)
if err != nil {
return nil, &NotFoundError{
name: c.name,
}
}
outerr := &bytes.Buffer{}
if c.stderr == nil {
c.stderr = outerr
} else {
c.stderr = io.MultiWriter(c.stderr, outerr)
}
var cmd *exec.Cmd
if c.ctx != nil {
cmd = exec.CommandContext(c.ctx, bin, args...)
} else {
cmd = exec.Command(bin, args...)
}
cmd.Stdin = c.stdin
cmd.Stderr = c.stderr
cmd.Stdout = c.stdout
cmd.Env = c.env
cmd.Dir = c.dir
return &cmdWrapper{outerr: outerr, c: cmd, name: c.name}, nil
}
// InPath reports whether binaryName is in $PATH.
func InPath(binaryName string) bool {
if strings.Contains(binaryName, "/") {
panic("binary name should not contain any slash")
}
_, err := safeexec.LookPath(binaryName)
return err == nil
}
// LookPath finds the path to binaryName in $PATH.
// Returns "" if not found.
func LookPath(binaryName string) string {
if strings.Contains(binaryName, "/") {
panic("binary name should not contain any slash")
}
s, err := safeexec.LookPath(binaryName)
if err != nil {
return ""
}
return s
}
-45
View File
@@ -1,45 +0,0 @@
// Copyright 2020 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 hexec
import (
"context"
"os/exec"
"github.com/cli/safeexec"
)
// SafeCommand is a wrapper around os/exec Command which uses a LookPath
// implementation that does not search in current directory before looking in PATH.
// See https://github.com/cli/safeexec and the linked issues.
func SafeCommand(name string, arg ...string) (*exec.Cmd, error) {
bin, err := safeexec.LookPath(name)
if err != nil {
return nil, err
}
return exec.Command(bin, arg...), nil
}
// SafeCommandContext wraps CommandContext
// See SafeCommand for more context.
func SafeCommandContext(ctx context.Context, name string, arg ...string) (*exec.Cmd, error) {
bin, err := safeexec.LookPath(name)
if err != nil {
return nil, err
}
return exec.CommandContext(ctx, bin, arg...), 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
}
+2 -3
View File
@@ -14,13 +14,12 @@
package hugio
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/spf13/afero"
)
@@ -60,7 +59,7 @@ func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool
}
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
+5
View File
@@ -31,6 +31,11 @@ type ReadSeekCloser interface {
io.Closer
}
// ReadSeekCloserProvider provides a ReadSeekCloser.
type ReadSeekCloserProvider interface {
ReadSeekCloser() (ReadSeekCloser, error)
}
// ReadSeekerNoOpCloser implements ReadSeekCloser by doing nothing in Close.
// TODO(bep) rename this and similar to ReadSeekerNopCloser, naming used in stdlib, which kind of makes sense.
type ReadSeekerNoOpCloser struct {
+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
+114 -16
View File
@@ -21,6 +21,8 @@ import (
"runtime/debug"
"sort"
"strings"
"sync"
"time"
"github.com/gohugoio/hugo/hugofs/files"
@@ -36,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
)
@@ -57,6 +54,11 @@ type Info struct {
// This can also be set by the user.
// 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
}
// Version returns the current version as a comparable version string.
@@ -77,20 +79,42 @@ func (i Info) IsExtended() bool {
return IsExtended
}
// Deps gets a list of dependencies for this Hugo build.
func (i Info) Deps() []*Dependency {
return i.deps
}
// NewInfo creates a new Hugo Info object.
func NewInfo(environment string) Info {
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,
}
}
// GetExecEnviron creates and gets the common os/exec environment used in the
// external programs we interact with via os/exec, e.g. postcss.
func GetExecEnviron(workDir string, cfg config.Provider, fs afero.Fs) []string {
env := os.Environ()
var env []string
nodepath := filepath.Join(workDir, "node_modules")
if np := os.Getenv("NODE_PATH"); np != "" {
nodepath = workDir + string(os.PathListSeparator) + np
@@ -98,18 +122,68 @@ func GetExecEnviron(workDir string, cfg config.Provider, fs afero.Fs) []string {
config.SetEnvVars(&env, "NODE_PATH", nodepath)
config.SetEnvVars(&env, "PWD", workDir)
config.SetEnvVars(&env, "HUGO_ENVIRONMENT", cfg.GetString("environment"))
fis, err := afero.ReadDir(fs, files.FolderJSConfig)
if err == nil {
for _, fi := range fis {
key := fmt.Sprintf("HUGO_FILE_%s", strings.ReplaceAll(strings.ToUpper(fi.Name()), ".", "_"))
value := fi.(hugofs.FileMetaInfo).Meta().Filename
config.SetEnvVars(&env, key, value)
config.SetEnvVars(&env, "HUGO_ENV", cfg.GetString("environment"))
if fs != nil {
fis, err := afero.ReadDir(fs, files.FolderJSConfig)
if err == nil {
for _, fi := range fis {
key := fmt.Sprintf("HUGO_FILE_%s", strings.ReplaceAll(strings.ToUpper(fi.Name()), ".", "_"))
value := fi.(hugofs.FileMetaInfo).Meta().Filename
config.SetEnvVars(&env, key, value)
}
}
}
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 {
@@ -128,8 +202,8 @@ func GetDependencyList() []string {
)
}
bi, ok := debug.ReadBuildInfo()
if !ok {
bi := getBuildInfo()
if bi == nil {
return deps
}
@@ -151,3 +225,27 @@ func IsRunningAsTest() bool {
}
return false
}
// Dependency is a single dependency, which can be either a Hugo Module or a local theme.
type Dependency struct {
// Returns the path to this module.
// This will either be the module path, e.g. "github.com/gohugoio/myshortcodes",
// or the path below your /theme folder, e.g. "mytheme".
Path string
// The module version.
Version string
// Whether this dependency is vendored.
Vendor bool
// Time version was created.
Time time.Time
// In the dependency tree, this is the first module that defines this module
// as a dependency.
Owner *Dependency
// Replaced by this dependency.
Replace *Dependency
}
+9 -4
View File
@@ -23,17 +23,22 @@ import (
func TestHugoInfo(t *testing.T) {
c := qt.New(t)
hugoInfo := NewInfo("")
hugoInfo := NewInfo("", nil)
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)
c.Assert(hugoInfo.IsExtended(), qt.Equals, IsExtended)
devHugoInfo := NewInfo("development")
devHugoInfo := NewInfo("development", nil)
c.Assert(devHugoInfo.IsProduction(), qt.Equals, false)
}
+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.89,
PatchLevel: 2,
Suffix: "",
Major: 0,
Minor: 102,
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
+39 -23
View File
@@ -29,8 +29,11 @@ import (
jww "github.com/spf13/jwalterweatherman"
)
// Counts ERROR logs to the global jww logger.
var GlobalErrorCounter *jww.Counter
var (
// Counts ERROR logs to the global jww logger.
GlobalErrorCounter *jww.Counter
PanicOnWarning bool
)
func init() {
GlobalErrorCounter = &jww.Counter{}
@@ -55,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
@@ -98,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...)
}
@@ -110,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...)
}
@@ -130,23 +133,31 @@ func (l *logger) Info() *log.Logger {
return l.INFO
}
func (l *logger) Warnf(format string, v ...interface{}) {
const panicOnWarningMessage = "Warning trapped. Remove the --panicOnWarning flag to continue."
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)
}
}
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...)
}
@@ -229,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)")
@@ -274,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)},
+8 -55
View File
@@ -16,7 +16,6 @@ package paths
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"regexp"
@@ -36,8 +35,7 @@ type filepathPathBridge interface {
Separator() string
}
type filepathBridge struct {
}
type filepathBridge struct{}
func (filepathBridge) Base(in string) string {
return filepath.Base(in)
@@ -65,9 +63,13 @@ func (filepathBridge) Separator() string {
var fpb filepathBridge
// ToSlashTrimLeading is just a filepath.ToSlash with an added / prefix trimmer.
func ToSlashTrimLeading(s string) string {
return strings.TrimPrefix(filepath.ToSlash(s), "/")
// 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
@@ -233,23 +235,6 @@ func GetRelativePath(path, base string) (final string, err error) {
return name, nil
}
// PathPrep prepares the path using the uglify setting to create paths on
// either the form /section/name/index.html or /section/name.html.
func PathPrep(ugly bool, in string) string {
if ugly {
return Uglify(in)
}
return PrettifyPath(in)
}
// PrettifyPath is the same as PrettifyURLPath but for file paths.
// /section/name.html becomes /section/name/index.html
// /section/name/ becomes /section/name/index.html
// /section/name/index.html becomes /section/name/index.html
func PrettifyPath(in string) string {
return prettifyPath(in, fpb)
}
func prettifyPath(in string, b filepathPathBridge) string {
if filepath.Ext(in) == "" {
// /section/name/ -> /section/name/index.html
@@ -278,35 +263,3 @@ func (n NamedSlice) String() string {
}
return fmt.Sprintf("%s%s{%s}", n.Name, FilePathSeparator, strings.Join(n.Slice, ","))
}
// FindCWD returns the current working directory from where the Hugo
// executable is run.
func FindCWD() (string, error) {
serverFile, err := filepath.Abs(os.Args[0])
if err != nil {
return "", fmt.Errorf("can't get absolute path for executable: %v", err)
}
path := filepath.Dir(serverFile)
realFile, err := filepath.EvalSymlinks(serverFile)
if err != nil {
if _, err = os.Stat(serverFile + ".exe"); err == nil {
realFile = filepath.Clean(serverFile + ".exe")
}
}
if err == nil && realFile != serverFile {
path = filepath.Dir(realFile)
}
return path, nil
}
// AddTrailingSlash adds a trailing Unix styled slash (/) if not already
// there.
func AddTrailingSlash(path string) string {
if !strings.HasSuffix(path, "/") {
path += "/"
}
return path
}
+1 -25
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/")},
@@ -226,27 +226,3 @@ func TestFileAndExt(t *testing.T) {
}
}
}
func TestFindCWD(t *testing.T) {
type test struct {
expectedDir string
expectedErr error
}
// cwd, _ := os.Getwd()
data := []test{
//{cwd, nil},
// Commenting this out. It doesn't work properly.
// There's a good reason why we don't use os.Getwd(), it doesn't actually work the way we want it to.
// I really don't know a better way to test this function. - SPF 2014.11.04
}
for i, d := range data {
dir, err := FindCWD()
if d.expectedDir != dir {
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedDir, dir)
}
if d.expectedErr != err {
t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedErr, err)
}
}
}
+28 -59
View File
@@ -17,13 +17,11 @@ import (
"fmt"
"net/url"
"path"
"path/filepath"
"strings"
"github.com/PuerkitoBio/purell"
)
type pathBridge struct {
}
type pathBridge struct{}
func (pathBridge) Base(in string) string {
return path.Base(in)
@@ -51,51 +49,6 @@ func (pathBridge) Separator() string {
var pb pathBridge
func sanitizeURLWithFlags(in string, f purell.NormalizationFlags) string {
s, err := purell.NormalizeURLString(in, f)
if err != nil {
return in
}
// Temporary workaround for the bug fix and resulting
// behavioral change in purell.NormalizeURLString():
// a leading '/' was inadvertently added to relative links,
// but no longer, see #878.
//
// I think the real solution is to allow Hugo to
// make relative URL with relative path,
// e.g. "../../post/hello-again/", as wished by users
// in issues #157, #622, etc., without forcing
// relative URLs to begin with '/'.
// Once the fixes are in, let's remove this kludge
// and restore SanitizeURL() to the way it was.
// -- @anthonyfok, 2015-02-16
//
// Begin temporary kludge
u, err := url.Parse(s)
if err != nil {
panic(err)
}
if len(u.Path) > 0 && !strings.HasPrefix(u.Path, "/") {
u.Path = "/" + u.Path
}
return u.String()
// End temporary kludge
// return s
}
// SanitizeURL sanitizes the input URL string.
func SanitizeURL(in string) string {
return sanitizeURLWithFlags(in, purell.FlagsSafe|purell.FlagRemoveTrailingSlash|purell.FlagRemoveDotSegments|purell.FlagRemoveDuplicateSlashes|purell.FlagRemoveUnnecessaryHostDots|purell.FlagRemoveEmptyPortSeparator)
}
// SanitizeURLKeepTrailingSlash is the same as SanitizeURL, but will keep any trailing slash.
func SanitizeURLKeepTrailingSlash(in string) string {
return sanitizeURLWithFlags(in, purell.FlagsSafe|purell.FlagRemoveDotSegments|purell.FlagRemoveDuplicateSlashes|purell.FlagRemoveUnnecessaryHostDots|purell.FlagRemoveEmptyPortSeparator)
}
// MakePermalink combines base URL with content path to create full URL paths.
// Example
// base: http://spf13.com/
@@ -127,16 +80,6 @@ func MakePermalink(host, plink string) *url.URL {
return base
}
// IsAbsURL determines whether the given path points to an absolute URL.
func IsAbsURL(path string) bool {
url, err := url.Parse(path)
if err != nil {
return false
}
return url.IsAbs() || strings.HasPrefix(path, "//")
}
// AddContextRoot adds the context root to an URL if it's not already set.
// For relative URL entries on sites with a base url with a context root set (i.e. http://example.com/mysite),
// relative URLs must not include the context root if canonifyURLs is enabled. But if it's disabled, it must be set.
@@ -210,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
}
-30
View File
@@ -14,41 +14,11 @@
package paths
import (
"strings"
"testing"
qt "github.com/frankban/quicktest"
)
func TestSanitizeURL(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"http://foo.bar/", "http://foo.bar"},
{"http://foo.bar", "http://foo.bar"}, // issue #1105
{"http://foo.bar/zoo/", "http://foo.bar/zoo"}, // issue #931
}
for i, test := range tests {
o1 := SanitizeURL(test.input)
o2 := SanitizeURLKeepTrailingSlash(test.input)
expected2 := test.expected
if strings.HasSuffix(test.input, "/") && !strings.HasSuffix(expected2, "/") {
expected2 += "/"
}
if o1 != test.expected {
t.Errorf("[%d] 1: Expected %#v, got %#v\n", i, test.expected, o1)
}
if o2 != expected2 {
t.Errorf("[%d] 2: Expected %#v, got %#v\n", i, expected2, o2)
}
}
}
func TestMakePermalink(t *testing.T) {
type test struct {
host, link, output string
+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)
}
@@ -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.
@@ -11,27 +11,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package commands
package hstring
import (
"github.com/spf13/cobra"
)
type RenderedString string
var _ cmder = (*checkCmd)(nil)
type checkCmd struct {
*baseCmd
}
func newCheckCmd() *checkCmd {
cc := &checkCmd{baseCmd: &baseCmd{
cmd: &cobra.Command{
Use: "check",
Short: "Contains some verification checks",
},
}}
cc.cmd.AddCommand(newLimitCmd().getCommand())
return cc
func (s RenderedString) String() string {
return string(s)
}
@@ -1,4 +1,4 @@
// Copyright 2019 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,21 +11,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package maps
package hstring
import (
"html/template"
"testing"
qt "github.com/frankban/quicktest"
"github.com/spf13/cast"
)
// GetString tries to get a value with key from map m and convert it to a string.
// It will return an empty string if not found or if it cannot be convertd to a string.
func GetString(m map[string]interface{}, key string) string {
if m == nil {
return ""
}
v, found := m[key]
if !found {
return ""
}
return cast.ToString(v)
func TestRenderedString(t *testing.T) {
c := qt.New(t)
// 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)
}

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