mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-25 15:58:53 +00:00
Compare commits
180 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 10ddc2ef92 | |||
| c406fd3a0e | |||
| 286821e360 | |||
| 79f15be5b0 | |||
| 5c2e014a51 | |||
| cc44583cc3 | |||
| 4da672af88 | |||
| f1886f8c37 | |||
| 5bec50838c | |||
| f650e4d751 | |||
| c934a45069 | |||
| 91b02091a5 | |||
| 8966424e0e | |||
| 72510969a6 | |||
| 70c5e485bb | |||
| a78b17d7f1 | |||
| a481942532 | |||
| 0f921ace6f | |||
| 6019953769 | |||
| 92e86702ea | |||
| 6c9ea022a9 | |||
| 12d3469dd1 | |||
| bf7ee8a91a | |||
| d912491f2d | |||
| 857374e693 | |||
| c27639b9a0 | |||
| ceb486f98c | |||
| 5afc89f2bf | |||
| c1eac616d5 | |||
| 3c8256a137 | |||
| 19d76ae96b | |||
| 11ecea6106 | |||
| da98724bc8 | |||
| 4d470bb732 | |||
| 0ff8e13c1c | |||
| ffd37d4f75 | |||
| b4b65245b2 | |||
| 87886f40d1 | |||
| 67caf50698 | |||
| 7917961d59 | |||
| 80ecb95895 | |||
| 58e09cc6c7 | |||
| 635cc346ce | |||
| 12646750aa | |||
| 79639c981c | |||
| 9b313cec1b | |||
| b74b8d6478 | |||
| 793e38f5ce | |||
| 7f698c8934 | |||
| fa0e16f4c7 | |||
| 12e4c4d5dc | |||
| bac03f4076 | |||
| cc14c6a52c | |||
| e3308a0bbc | |||
| 019299b0b0 | |||
| 23ed087c4e | |||
| a018259bcf | |||
| ad5e04daa4 | |||
| e9b716ad98 | |||
| ae31dbdd1e | |||
| 5b4bfc2db7 | |||
| 92f55f1121 | |||
| 078226dd66 | |||
| 06d228aad1 | |||
| b1016d2e26 | |||
| 49336bfc58 | |||
| 941818295d | |||
| 5491e55477 | |||
| bf7af9043d | |||
| 5e12bf7dc3 | |||
| 9df2ec7988 | |||
| 59300faae4 | |||
| 9f98b3e718 | |||
| 3ab84651c6 | |||
| 7241b5fd51 | |||
| 6dfbd24797 | |||
| f59c3c0214 | |||
| 3ca29b1563 | |||
| fdb0b7fb19 | |||
| 49dd53a405 | |||
| 68d9d3ebd7 | |||
| 9009c8cdca | |||
| 12dc9a6e4a | |||
| 1b85303ac2 | |||
| 8a04d47ab7 | |||
| 7c9fada778 | |||
| 0e79446586 | |||
| aaf2e9693a | |||
| ee359df172 | |||
| 0f989d5e21 | |||
| f73c567534 | |||
| d178fe94fe | |||
| 732dcb848f | |||
| 944859f1ad | |||
| 90b2674ddc | |||
| bb9377b5e5 | |||
| e88f1b80b8 | |||
| 516f0cb6ce | |||
| 21d17566a3 | |||
| 35e9b3ed1e | |||
| a0009e070a | |||
| 3c1deaf201 | |||
| 7bed16c30f | |||
| 2ba2271e4a | |||
| 60a2cdf72d | |||
| e08cfc8ca0 | |||
| ef147f4e87 | |||
| 489519566e | |||
| 91c0b0f76b | |||
| baef235d50 | |||
| 0541a1b57d | |||
| 258884f44f | |||
| 254c2b323c | |||
| 84f71ba8ff | |||
| 69f0e88a4a | |||
| 82adc972e8 | |||
| 60533fdc06 | |||
| 261143bbbc | |||
| b8526f32fa | |||
| ed7e250068 | |||
| 6a09e7f28e | |||
| 29e5cbb699 | |||
| ded6866001 | |||
| e1d43021ca | |||
| 5db215d4d6 | |||
| b7dc93ca11 | |||
| f210188da3 | |||
| c782ebd89c | |||
| 73779707a2 | |||
| 085c1b3d61 | |||
| 6c955cc3df | |||
| 5446900dee | |||
| cf38c73f53 | |||
| 536bf71abe | |||
| ea3c95a7b0 | |||
| 5e5ce00d41 | |||
| a191b38ac8 | |||
| 382c726e63 | |||
| 4c46f9400b | |||
| 8c7a4e995b | |||
| 2ca0fcc448 | |||
| a6d774e809 | |||
| dfb1895e4b | |||
| 9e4072ac23 | |||
| 0ef2952846 | |||
| e3ae8f025d | |||
| 06faee5beb | |||
| 409c6c3fcd | |||
| ff77a927f9 | |||
| 9cdca1f958 | |||
| 6462eecfbd | |||
| a7d6b1413f | |||
| 3f497d496f | |||
| 32585696be | |||
| d47225ce9e | |||
| e3dfc76fa8 | |||
| cd59216de4 | |||
| e285153d7f | |||
| 20ea2e0c65 | |||
| 51d0a0ab01 | |||
| fd099331ec | |||
| 43f1282e73 | |||
| e96cdfe966 | |||
| ffdbce5787 | |||
| a838a27e4c | |||
| d3927310d5 | |||
| 273d9f69a6 | |||
| a6257d8a44 | |||
| 6c2db0dfb0 | |||
| 8f293a1855 | |||
| 901cd970db | |||
| f86b5f70a6 | |||
| 3297b395d8 | |||
| ba6f74e404 | |||
| 231374a1fa | |||
| 5adc837904 | |||
| f89108f2b1 | |||
| dd67922013 | |||
| 9a235d0afc | |||
| 99407c39ba |
@@ -0,0 +1,73 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
name: TestDartSassV1
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org
|
||||
GO111MODULE: on
|
||||
DART_SASS_VERSION: 1.62.1
|
||||
DART_SASS_SHA_LINUX: 3574da75a7322a539034648b8ff84ff2cca162eb924d72b663d718cd3936f075
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.20.x]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
cache: true
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@ee2113536afb7f793eed4ce60e8d3b26db912da4
|
||||
with:
|
||||
ruby-version: '2.7'
|
||||
bundler-cache: true #
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@3105fb18c05ddd93efea5f9e0bef7a03a6e9e7df
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Mage
|
||||
run: go install github.com/magefile/mage@v1.15.0
|
||||
- name: Install asciidoctor
|
||||
uses: reitzig/actions-asciidoctor@7570212ae20b63653481675fb1ff62d1073632b0
|
||||
- name: Install docutils
|
||||
run: |
|
||||
pip install docutils
|
||||
rst2html.py --version
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install pandoc on Linux
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y pandoc
|
||||
- if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
brew install pandoc
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
Choco-Install -PackageName pandoc
|
||||
- run: pandoc -v
|
||||
- name: Install dart-sass-embedded Linux
|
||||
run: |
|
||||
echo "Install Dart Sass version ${DART_SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${DART_SASS_VERSION}/sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_LINUX} sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
|
||||
tar -xvf "sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/sass_embedded/" >> $GITHUB_PATH
|
||||
- name: Check
|
||||
run: |
|
||||
dart-sass-embedded --version
|
||||
mage -v check;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
+43
-24
@@ -6,10 +6,10 @@ name: Test
|
||||
env:
|
||||
GOPROXY: https://proxy.golang.org
|
||||
GO111MODULE: on
|
||||
DART_SASS_VERSION: 1.56.2
|
||||
DART_SASS_SHA_LINUX: 9e4f455f7b8619959d7878af2862383be58392eb963a14ff87cc512c03701e2a
|
||||
DART_SASS_SHA_MACOS: 5992e979e2c30ec363f8e338822bb2b4443c74232b3340501a76180f5652cb09
|
||||
DART_SASS_SHA_WINDOWS: 8d3d9117c54840e3e6a4919e43acf75ea52f28a64fc87a8e29d80ec72ee36cfb
|
||||
SASS_VERSION: 1.63.2
|
||||
DART_SASS_SHA_LINUX: 3ea33c95ad5c35fda6e9a0956199eef38a398f496cfb8750e02479d7d1dd42af
|
||||
DART_SASS_SHA_MACOS: 11c70f259836b250b44a9cb57fed70e030f21f45069b467d371685855f1eb4f0
|
||||
DART_SASS_SHA_WINDOWS: cd8cd36a619dd8e27f93d3186c52d70eb7d69472aa6c85f5094b29693e773f64
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
@@ -21,9 +21,9 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@268d8c0ca0432bb2cf416faae41297df9d262d7f
|
||||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
check-latest: true
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Mage
|
||||
run: go install github.com/magefile/mage@07afc7d24f4d6d6442305d49552f04fbda5ccb3e
|
||||
run: go install github.com/magefile/mage@v1.15.0
|
||||
- name: Install asciidoctor
|
||||
uses: reitzig/actions-asciidoctor@7570212ae20b63653481675fb1ff62d1073632b0
|
||||
- name: Install docutils
|
||||
@@ -62,33 +62,52 @@ jobs:
|
||||
- run: pandoc -v
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
Choco-Install -PackageName mingw -ArgumentList "--version","10.2.0","--allow-downgrade"
|
||||
Choco-Install -PackageName mingw -ArgumentList "--version","12.2.0","--allow-downgrade"
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install dart-sass-embedded Linux
|
||||
run: |
|
||||
echo "Install Dart Sass version ${DART_SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${DART_SASS_VERSION}/sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_LINUX} sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
|
||||
tar -xvf "sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/sass_embedded/" >> $GITHUB_PATH
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_LINUX} dart-sass-${SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c;
|
||||
tar -xvf "dart-sass-${SASS_VERSION}-linux-x64.tar.gz";
|
||||
echo "$GOBIN"
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'macos-latest'
|
||||
name: Install dart-sass-embedded MacOS
|
||||
run: |
|
||||
echo "Install Dart Sass version ${DART_SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${DART_SASS_VERSION}/sass_embedded-${DART_SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_MACOS} sass_embedded-${DART_SASS_VERSION}-macos-x64.tar.gz" | shasum -a 256 -c;
|
||||
tar -xvf "sass_embedded-${DART_SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/sass_embedded/" >> $GITHUB_PATH
|
||||
echo "Install Dart Sass version ${SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "${DART_SASS_SHA_MACOS} dart-sass-${SASS_VERSION}-macos-x64.tar.gz" | shasum -a 256 -c;
|
||||
tar -xvf "dart-sass-${SASS_VERSION}-macos-x64.tar.gz";
|
||||
echo "$GITHUB_WORKSPACE/dart-sass/" >> $GITHUB_PATH
|
||||
- if: matrix.os == 'windows-latest'
|
||||
name: Install dart-sass-embedded Windows
|
||||
run: |
|
||||
echo "Install Dart Sass version ${env:DART_SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${env:DART_SASS_VERSION}/sass_embedded-${env:DART_SASS_VERSION}-windows-x64.zip";
|
||||
Expand-Archive -Path "sass_embedded-${env:DART_SASS_VERSION}-windows-x64.zip" -DestinationPath .;
|
||||
echo "$env:GITHUB_WORKSPACE/sass_embedded/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append
|
||||
- name: Check
|
||||
echo "Install Dart Sass version ${env:SASS_VERSION} ..."
|
||||
curl -LJO "https://github.com/sass/dart-sass/releases/download/${env:SASS_VERSION}/dart-sass-${env:SASS_VERSION}-windows-x64.zip";
|
||||
Expand-Archive -Path "dart-sass-${env:SASS_VERSION}-windows-x64.zip" -DestinationPath .;
|
||||
echo "$env:GITHUB_WORKSPACE/dart-sass/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append
|
||||
- if: matrix.os != 'windows-latest'
|
||||
name: Check
|
||||
run: |
|
||||
sass --version;
|
||||
mage -v check;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
|
||||
- if: matrix.os == 'windows-latest'
|
||||
# See issue #11052. We limit the build to regular test (no -race flag) on Windows for now.
|
||||
name: Test
|
||||
run: |
|
||||
mage -v test;
|
||||
env:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
- name: Build tags
|
||||
run: |
|
||||
go install -tags extended,nodeploy
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Build for dragonfly
|
||||
run: |
|
||||
go install
|
||||
env:
|
||||
GOARCH: amd64
|
||||
GOOS: dragonfly
|
||||
|
||||
@@ -1,243 +1,290 @@
|
||||
[bep]: https://github.com/spf13
|
||||
[bugs]: https://github.com/gohugoio/hugo/issues?q=is%3Aopen+is%3Aissue+label%3ABug
|
||||
[contributing]: CONTRIBUTING.md
|
||||
[create a proposal]: https://github.com/gohugoio/hugo/issues/new?labels=Proposal%2C+NeedsTriage&template=feature_request.md
|
||||
[documentation repository]: https://github.com/gohugoio/hugoDocs
|
||||
[documentation]: https://gohugo.io/documentation
|
||||
[dragonfly bsd, freebsd, netbsd, and openbsd]: https://gohugo.io/installation/bsd
|
||||
[forum]: https://discourse.gohugo.io
|
||||
[friends]: https://github.com/gohugoio/hugo/graphs/contributors
|
||||
[go]: https://go.dev/
|
||||
[hugo modules]: https://gohugo.io/hugo-modules/
|
||||
[installation]: https://gohugo.io/installation
|
||||
[issue queue]: https://github.com/gohugoio/hugo/issues
|
||||
[linux]: https://gohugo.io/installation/linux
|
||||
[macos]: https://gohugo.io/installation/macos
|
||||
[prebuilt binary]: https://github.com/gohugoio/hugo/releases/latest
|
||||
[requesting help]: https://discourse.gohugo.io/t/requesting-help/9132
|
||||
[spf13]: https://github.com/spf13
|
||||
[static site generator]: https://en.wikipedia.org/wiki/Static_site_generator
|
||||
[support]: https://discourse.gohugo.io
|
||||
[themes]: https://themes.gohugo.io/
|
||||
[twitter]: https://twitter.com/gohugoio
|
||||
[website]: https://gohugo.io
|
||||
[windows]: https://gohugo.io/installation/windows
|
||||
|
||||
<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](https://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go](https://go.dev/).
|
||||
A fast and flexible static site generator built with love by [bep], [spf13], and [friends] in [Go].
|
||||
|
||||
[Website](https://gohugo.io) |
|
||||
[Forum](https://discourse.gohugo.io) |
|
||||
[Documentation](https://gohugo.io/getting-started/) |
|
||||
[Installation Guide](https://gohugo.io/getting-started/installing/) |
|
||||
[Contribution Guide](CONTRIBUTING.md) |
|
||||
[Twitter](https://twitter.com/gohugoio)
|
||||
---
|
||||
|
||||
[](https://godoc.org/github.com/gohugoio/hugo)
|
||||
[](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
|
||||
[](https://goreportcard.com/report/github.com/gohugoio/hugo)
|
||||
|
||||
* [Overview](#overview)
|
||||
* [Banner Sponsors](#banner-sponsors)
|
||||
* [Supported Architectures](#supported-architectures)
|
||||
* [Choose How to Install](#choose-how-to-install)
|
||||
* [Install Hugo as Your Site Generator (Binary Install)](#install-hugo-as-your-site-generator-binary-install)
|
||||
* [Build and Install the Binary from Source (Using the Go toolchain)](#build-and-install-the-binary-from-source-using-the-go-toolchain)
|
||||
* [The Hugo Documentation](#the-hugo-documentation)
|
||||
* [Contributing to Hugo](#contributing-code-to-hugo)
|
||||
* [Dependencies](#dependencies)
|
||||
[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | [Twitter]
|
||||
|
||||
## Overview
|
||||
|
||||
Hugo is a static HTML and CSS website generator written in [Go](https://go.dev/).
|
||||
It is optimized for speed, ease of use, and configurability.
|
||||
Hugo takes a directory with content and templates and renders them into a full HTML website.
|
||||
Hugo is a [static site generator] written in [Go], optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a complete site in seconds, often less.
|
||||
|
||||
Hugo relies on Markdown files with front matter for metadata, and you can run Hugo from any directory.
|
||||
This works well for shared hosts and other systems where you don’t have a privileged account.
|
||||
Due to its flexible framework, multilingual support, and powerful taxonomy system, Hugo is widely used to create:
|
||||
|
||||
Hugo renders a typical website of moderate size in a fraction of a second.
|
||||
A good rule of thumb is that each piece of content renders in around 1 millisecond.
|
||||
- Corporate, government, nonprofit, education, news, event, and project sites
|
||||
- Documentation sites
|
||||
- Image portfolios
|
||||
- Landing pages
|
||||
- Business, professional, and personal blogs
|
||||
- Resumes and CVs
|
||||
|
||||
Hugo is designed to work well for any kind of website including blogs, tumbles, and docs.
|
||||
Use Hugo's embedded web server during development to instantly see changes to content, structure, behavior, and presentation. Then deploy the site to your host, or push changes to your Git provider for automated builds and deployment.
|
||||
|
||||
Hugo's fast asset pipelines include:
|
||||
|
||||
- CSS bundling – transpilation (Sass), tree shaking, minification, source maps, SRI hashing, and PostCSS integration
|
||||
- JavaScript bundling – transpilation (TypeScript, JSX), tree shaking, minification, source maps, and SRI hashing
|
||||
- Image processing – convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract EXIF data
|
||||
|
||||
And with [Hugo Modules], you can share content, assets, data, translations, themes, templates, and configuration with other projects via public or private Git repositories.
|
||||
|
||||
## Sponsors
|
||||
|
||||
## Banner Sponsors
|
||||
<p> </p>
|
||||
<p float="left">
|
||||
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
|
||||
<a href="https://cloudcannon.com/hugo-cms/?utm_campaign=HugoSponsorship&utm_source=sponsor&utm_content=gohugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/cloudcannon-blue.svg" width="220" alt="CloudCannon"></a>
|
||||
<p> </p>
|
||||
|
||||
## Supported Architectures
|
||||
## Installation
|
||||
|
||||
Currently, we provide pre-built Hugo binaries for Windows, Linux, FreeBSD, NetBSD, DragonFly BSD, OpenBSD, macOS (Darwin), and [Android](https://gist.github.com/bep/a0d8a26cf6b4f8bc992729b8e50b480b) for x64, i386 and ARM architectures.
|
||||
Install Hugo from a [prebuilt binary], package manager, or package repository. Please see the installation instructions for your operating system:
|
||||
|
||||
Hugo may also be compiled from source wherever the Go compiler tool chain can run, e.g. for other operating systems including Plan 9 and Solaris.
|
||||
- [macOS]
|
||||
- [Linux]
|
||||
- [Windows]
|
||||
- [DragonFly BSD, FreeBSD, NetBSD, and OpenBSD]
|
||||
|
||||
**Complete documentation is available at [Hugo Documentation](https://gohugo.io/getting-started/).**
|
||||
## Build from source
|
||||
|
||||
## Choose How to Install
|
||||
Hugo is available in two editions: standard and extended. With the extended edition you can:
|
||||
|
||||
If you want to use Hugo as your site generator, simply install the Hugo binaries.
|
||||
- Encode to the WebP format when processing images. You can decode WebP images with either edition.
|
||||
- Transpile Sass to CSS using the embedded LibSass transpiler. The extended edition is not required to use the Dart Sass transpiler.
|
||||
|
||||
To contribute to the Hugo source code or documentation, you should [fork the Hugo GitHub project](https://github.com/gohugoio/hugo#fork-destination-box) and clone it to your local machine.
|
||||
Prerequisites to build Hugo from source:
|
||||
|
||||
Finally, you can install the Hugo source code with `go`, build the binaries yourself, and run Hugo that way.
|
||||
Building the binaries is an easy task for an experienced `go` getter.
|
||||
- Standard edition: Go 1.19 or later
|
||||
- Extended edition: Go 1.19 or later, and GCC
|
||||
|
||||
### Install Hugo as Your Site Generator (Binary Install)
|
||||
Build the standard edition:
|
||||
|
||||
Use the [installation instructions in the Hugo documentation](https://gohugo.io/getting-started/installing/).
|
||||
|
||||
### Build and Install the Binary from Source (Using the Go toolchain)
|
||||
|
||||
#### Prerequisite Tools
|
||||
|
||||
* [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
|
||||
|
||||
To fetch, build and install from the Github source:
|
||||
|
||||
```bash
|
||||
```text
|
||||
go install github.com/gohugoio/hugo@latest
|
||||
```
|
||||
|
||||
If you want to compile with Sass/SCSS support use `-tags extended` and make sure `CGO_ENABLED=1` is set in your go environment. If you don't want to have CGO enabled, you may use the following command to temporarily enable CGO only for hugo compilation:
|
||||
Build the extended edition:
|
||||
|
||||
```bash
|
||||
```text
|
||||
CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest
|
||||
```
|
||||
|
||||
## The Hugo Documentation
|
||||
## Documentation
|
||||
|
||||
The Hugo documentation now lives in its own repository, see https://github.com/gohugoio/hugoDocs. But we do keep a version of that documentation as a `git subtree` in this repository. To build the sub folder `/docs` as a Hugo site, you need to clone this repo:
|
||||
Hugo's [documentation] includes installation instructions, a quick start guide, conceptual explanations, reference information, and examples.
|
||||
|
||||
```bash
|
||||
git clone git@github.com:gohugoio/hugo.git
|
||||
```
|
||||
## Contributing code to Hugo
|
||||
Please submit documentation issues and pull requests to the [documentation repository].
|
||||
|
||||
## Support
|
||||
|
||||
Please **do not use the issue queue** for questions or troubleshooting. Unless you are certain that your issue is a software defect, use the [forum].
|
||||
|
||||
Hugo’s [forum] is an active community of users and developers who answer questions, share knowledge, and provide examples. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help] before asking your first question.
|
||||
|
||||
## Contributing
|
||||
|
||||
You can contribute to the Hugo project by:
|
||||
|
||||
- Answering questions on the [forum]
|
||||
- Improving the [documentation]
|
||||
- Monitoring the [issue queue]
|
||||
- Creating or improving [themes]
|
||||
- Squashing [bugs]
|
||||
|
||||
Please submit documentation issues and pull requests to the [documentation repository].
|
||||
|
||||
If you have an idea for an enhancement or new feature, create a new topic on the [forum] in the "Feature" category. This will help you to:
|
||||
|
||||
- Determine if the capability already exists
|
||||
- Measure interest
|
||||
- Refine the concept
|
||||
|
||||
If there is sufficient interest, [create a proposal]. Do not submit a pull request until the project lead accepts the proposal.
|
||||
|
||||
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,
|
||||
organization, tutorials, blog posts, bug reports, issues, feature requests,
|
||||
feature implementations, pull requests, answering questions on the forum,
|
||||
helping to manage issues, etc.
|
||||
|
||||
The Hugo community and maintainers are [very active](https://github.com/gohugoio/hugo/pulse/monthly) and helpful, and the project benefits greatly from this activity.
|
||||
|
||||
## Asking Support Questions
|
||||
|
||||
We have an active [discussion forum](https://discourse.gohugo.io) where users and developers can ask questions.
|
||||
Please don't use the GitHub issue tracker to ask questions.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
If you believe you have found a defect in Hugo or its documentation, use
|
||||
the GitHub issue tracker to report the problem to the Hugo maintainers.
|
||||
If you're not sure if it's a bug or not, start by asking in the [discussion forum](https://discourse.gohugo.io).
|
||||
When reporting the issue, please provide the version of Hugo in use (`hugo version`).
|
||||
|
||||
## Dependencies
|
||||
|
||||
Hugo stands on the shoulder of many great open source libraries.
|
||||
Hugo stands on the shoulders of great open source libraries. Run `hugo env --logLevel info` to display a list of dependencies.
|
||||
|
||||
If you run `hugo env -v` you will get a complete and up to date list.
|
||||
<details>
|
||||
<summary>See current dependencies</summary>
|
||||
|
||||
In Hugo 0.111.2 that list is, in lexical order:
|
||||
|
||||
```
|
||||
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"
|
||||
```text
|
||||
cloud.google.com/go/compute/metadata="v0.2.3"
|
||||
cloud.google.com/go/iam="v1.1.0"
|
||||
cloud.google.com/go/storage="v1.30.1"
|
||||
cloud.google.com/go="v0.110.2"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore="v1.6.1"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity="v1.3.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal="v1.3.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob="v1.0.0"
|
||||
github.com/Azure/go-autorest/autorest/to="v0.4.0"
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go="v1.0.0"
|
||||
github.com/BurntSushi/locker="v0.0.0-20171006230638-a6e239ea1c69"
|
||||
github.com/PuerkitoBio/purell="v1.1.1"
|
||||
github.com/PuerkitoBio/urlesc="v0.0.0-20170810143723-de5bf2ad4578"
|
||||
github.com/alecthomas/chroma/v2="v2.5.0"
|
||||
github.com/alecthomas/chroma/v2="v2.7.0"
|
||||
github.com/armon/go-radix="v1.0.0"
|
||||
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/aws/aws-sdk-go-v2/aws/protocol/eventstream="v1.4.10"
|
||||
github.com/aws/aws-sdk-go-v2/config="v1.18.27"
|
||||
github.com/aws/aws-sdk-go-v2/credentials="v1.13.26"
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds="v1.13.4"
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager="v1.11.70"
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources="v1.1.34"
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2="v2.4.28"
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini="v1.3.35"
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a="v1.0.26"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding="v1.9.11"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum="v1.1.29"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url="v1.9.28"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared="v1.14.3"
|
||||
github.com/aws/aws-sdk-go-v2/service/s3="v1.35.0"
|
||||
github.com/aws/aws-sdk-go-v2/service/sso="v1.12.12"
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc="v1.14.12"
|
||||
github.com/aws/aws-sdk-go-v2/service/sts="v1.19.2"
|
||||
github.com/aws/aws-sdk-go-v2="v1.18.1"
|
||||
github.com/aws/aws-sdk-go="v1.44.284"
|
||||
github.com/aws/smithy-go="v1.13.5"
|
||||
github.com/bep/clocks="v0.5.0"
|
||||
github.com/bep/debounce="v1.2.0"
|
||||
github.com/bep/gitmap="v1.1.2"
|
||||
github.com/bep/goat="v0.5.0"
|
||||
github.com/bep/godartsass="v0.16.0"
|
||||
github.com/bep/golibsass="v1.1.0"
|
||||
github.com/bep/godartsass/v2="v2.0.0"
|
||||
github.com/bep/godartsass="v1.2.0"
|
||||
github.com/bep/golibsass="v1.1.1"
|
||||
github.com/bep/gowebp="v0.2.0"
|
||||
github.com/bep/lazycache="v0.2.0"
|
||||
github.com/bep/logg="v0.2.0"
|
||||
github.com/bep/mclib="v1.20400.20402"
|
||||
github.com/bep/overlayfs="v0.6.0"
|
||||
github.com/bep/simplecobra="v0.3.2"
|
||||
github.com/bep/tmc="v0.5.1"
|
||||
github.com/clbanning/mxj/v2="v2.5.7"
|
||||
github.com/cli/safeexec="v1.0.0"
|
||||
github.com/cli/safeexec="v1.0.1"
|
||||
github.com/cpuguy83/go-md2man/v2="v2.0.2"
|
||||
github.com/disintegration/gift="v1.2.1"
|
||||
github.com/dlclark/regexp2="v1.7.0"
|
||||
github.com/dustin/go-humanize="v1.0.0"
|
||||
github.com/evanw/esbuild="v0.17.0"
|
||||
github.com/frankban/quicktest="v1.14.4"
|
||||
github.com/dlclark/regexp2="v1.10.0"
|
||||
github.com/dustin/go-humanize="v1.0.1"
|
||||
github.com/evanw/esbuild="v0.18.5"
|
||||
github.com/fatih/color="v1.15.0"
|
||||
github.com/frankban/quicktest="v1.14.5"
|
||||
github.com/fsnotify/fsnotify="v1.6.0"
|
||||
github.com/getkin/kin-openapi="v0.110.0"
|
||||
github.com/getkin/kin-openapi="v0.118.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.3.0"
|
||||
github.com/go-openapi/jsonpointer="v0.19.6"
|
||||
github.com/go-openapi/swag="v0.22.3"
|
||||
github.com/gobuffalo/flect="v1.0.2"
|
||||
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="v1.0.1"
|
||||
github.com/golang-jwt/jwt/v4="v4.0.0"
|
||||
github.com/golang-jwt/jwt/v4="v4.5.0"
|
||||
github.com/golang/groupcache="v0.0.0-20210331224755-41bb18bfe9da"
|
||||
github.com/golang/protobuf="v1.5.2"
|
||||
github.com/golang/protobuf="v1.5.3"
|
||||
github.com/google/go-cmp="v0.5.9"
|
||||
github.com/google/s2a-go="v0.1.4"
|
||||
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/googleapis/enterprise-certificate-proxy="v0.2.5"
|
||||
github.com/googleapis/gax-go/v2="v2.11.0"
|
||||
github.com/gorilla/websocket="v1.5.0"
|
||||
github.com/hairyhenderson/go-codeowners="v0.2.3-0.20201026200250-cdc7c0759690"
|
||||
github.com/hairyhenderson/go-codeowners="v0.3.0"
|
||||
github.com/hashicorp/golang-lru/v2="v2.0.1"
|
||||
github.com/invopop/yaml="v0.1.0"
|
||||
github.com/jdkato/prose="v1.2.1"
|
||||
github.com/jmespath/go-jmespath="v0.4.0"
|
||||
github.com/josharian/intern="v1.0.0"
|
||||
github.com/kr/pretty="v0.3.1"
|
||||
github.com/kr/text="v0.2.0"
|
||||
github.com/kyokomi/emoji/v2="v2.2.11"
|
||||
github.com/mailru/easyjson="v0.0.0-20190626092158-b2ccc519800e"
|
||||
github.com/kylelemons/godebug="v1.1.0"
|
||||
github.com/kyokomi/emoji/v2="v2.2.12"
|
||||
github.com/mailru/easyjson="v0.7.7"
|
||||
github.com/marekm4/color-extractor="v1.2.0"
|
||||
github.com/mattn/go-ieproxy="v0.0.1"
|
||||
github.com/mattn/go-isatty="v0.0.17"
|
||||
github.com/mattn/go-colorable="v0.1.13"
|
||||
github.com/mattn/go-isatty="v0.0.19"
|
||||
github.com/mattn/go-runewidth="v0.0.9"
|
||||
github.com/mitchellh/hashstructure="v1.1.0"
|
||||
github.com/mitchellh/mapstructure="v1.5.0"
|
||||
github.com/mohae/deepcopy="v0.0.0-20170929034955-c48cc78d4826"
|
||||
github.com/muesli/smartcrop="v0.3.0"
|
||||
github.com/niklasfasching/go-org="v1.6.5"
|
||||
github.com/niklasfasching/go-org="v1.7.0"
|
||||
github.com/olekukonko/tablewriter="v0.0.5"
|
||||
github.com/pelletier/go-toml/v2="v2.0.6"
|
||||
github.com/rogpeppe/go-internal="v1.9.0"
|
||||
github.com/pelletier/go-toml/v2="v2.0.8"
|
||||
github.com/perimeterx/marshmallow="v1.1.4"
|
||||
github.com/pkg/browser="v0.0.0-20210911075715-681adbf594b8"
|
||||
github.com/pkg/errors="v0.9.1"
|
||||
github.com/rogpeppe/go-internal="v1.10.1-0.20230508101108-a4f6fabd84c5"
|
||||
github.com/russross/blackfriday/v2="v2.1.0"
|
||||
github.com/rwcarlsen/goexif="v0.0.0-20190401172101-9e8deecbddbd"
|
||||
github.com/sanity-io/litter="v1.5.5"
|
||||
github.com/sass/dart-sass/compiler="1.63.6"
|
||||
github.com/sass/dart-sass/implementation="1.63.6"
|
||||
github.com/sass/dart-sass/protocol="2.1.0"
|
||||
github.com/sass/libsass="3.6.5"
|
||||
github.com/spf13/afero="v1.9.3"
|
||||
github.com/spf13/cast="v1.5.0"
|
||||
github.com/spf13/cobra="v1.6.1"
|
||||
github.com/spf13/afero="v1.9.5"
|
||||
github.com/spf13/cast="v1.5.1"
|
||||
github.com/spf13/cobra="v1.7.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.12.4"
|
||||
github.com/tdewolff/parse/v2="v2.6.5"
|
||||
github.com/tdewolff/minify/v2="v2.12.7"
|
||||
github.com/tdewolff/parse/v2="v2.6.6"
|
||||
github.com/webmproject/libwebp="v1.2.4"
|
||||
github.com/yuin/goldmark="v1.5.4"
|
||||
go.opencensus.io="v0.24.0"
|
||||
go.uber.org/atomic="v1.10.0"
|
||||
gocloud.dev="v0.24.0"
|
||||
golang.org/x/crypto="v0.3.0"
|
||||
golang.org/x/exp="v0.0.0-20221031165847-c99f073a8326"
|
||||
golang.org/x/image="v0.5.0"
|
||||
golang.org/x/net="v0.7.0"
|
||||
golang.org/x/oauth2="v0.2.0"
|
||||
golang.org/x/sync="v0.1.0"
|
||||
golang.org/x/sys="v0.5.0"
|
||||
golang.org/x/text="v0.7.0"
|
||||
golang.org/x/tools="v0.4.0"
|
||||
go.uber.org/atomic="v1.11.0"
|
||||
go.uber.org/automaxprocs="v1.5.2"
|
||||
gocloud.dev="v0.30.0"
|
||||
golang.org/x/crypto="v0.10.0"
|
||||
golang.org/x/exp="v0.0.0-20230321023759-10a507213a29"
|
||||
golang.org/x/image="v0.8.0"
|
||||
golang.org/x/mod="v0.10.0"
|
||||
golang.org/x/net="v0.11.0"
|
||||
golang.org/x/oauth2="v0.9.0"
|
||||
golang.org/x/sync="v0.3.0"
|
||||
golang.org/x/sys="v0.9.0"
|
||||
golang.org/x/text="v0.10.0"
|
||||
golang.org/x/tools="v0.9.3"
|
||||
golang.org/x/xerrors="v0.0.0-20220907171357-04be3eba64a2"
|
||||
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.1"
|
||||
google.golang.org/api="v0.128.0"
|
||||
google.golang.org/appengine="v1.6.7"
|
||||
google.golang.org/genproto/googleapis/api="v0.0.0-20230530153820-e85fd2cbaebc"
|
||||
google.golang.org/genproto/googleapis/rpc="v0.0.0-20230530153820-e85fd2cbaebc"
|
||||
google.golang.org/genproto="v0.0.0-20230530153820-e85fd2cbaebc"
|
||||
google.golang.org/grpc="v1.56.0"
|
||||
google.golang.org/protobuf="v1.30.0"
|
||||
gopkg.in/yaml.v2="v2.4.0"
|
||||
gopkg.in/yaml.v3="v3.0.1"
|
||||
software.sslmate.com/src/go-pkcs12="v0.2.0"
|
||||
```
|
||||
</details>
|
||||
|
||||
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
// Package cache contains the differenct cache implementations.
|
||||
// Package cache contains the different cache implementations.
|
||||
package cache
|
||||
|
||||
Vendored
+1
-1
@@ -81,7 +81,7 @@ type FileCacheConfig struct {
|
||||
// Max age of cache entries in this cache. Any items older than this will
|
||||
// be removed and not returned from the cache.
|
||||
// A negative value means forever, 0 means cache is disabled.
|
||||
// Hugo is leninent with what types it accepts here, but we recommend using
|
||||
// Hugo is lenient with what types it accepts here, but we recommend using
|
||||
// a duration string, a sequence of decimal numbers, each with optional fraction and a unit suffix,
|
||||
// such as "300ms", "1.5h" or "2h45m".
|
||||
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
|
||||
Vendored
+2
-3
@@ -16,11 +16,10 @@ package filecache_test
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
@@ -80,7 +79,7 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{T: t, TxtarString: files, Running: true, RunGC: true, NeedsOsFS: true, LogLevel: jww.LevelInfo},
|
||||
hugolib.IntegrationTestConfig{T: t, TxtarString: files, Running: true, RunGC: true, NeedsOsFS: true, LogLevel: logg.LevelInfo},
|
||||
).Build()
|
||||
|
||||
b.Assert(b.GCCount, qt.Equals, 0)
|
||||
|
||||
+85
-92
@@ -18,20 +18,21 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
|
||||
"go.uber.org/automaxprocs/maxprocs"
|
||||
|
||||
"github.com/bep/clock"
|
||||
"github.com/bep/clocks"
|
||||
"github.com/bep/lazycache"
|
||||
"github.com/bep/logg"
|
||||
"github.com/bep/overlayfs"
|
||||
"github.com/bep/simplecobra"
|
||||
|
||||
@@ -108,17 +109,11 @@ type rootCommand struct {
|
||||
buildWatch bool
|
||||
environment string
|
||||
|
||||
// File format to read or write (TOML, YAML, JSON).
|
||||
format string
|
||||
|
||||
// Common build flags.
|
||||
baseURL string
|
||||
gc bool
|
||||
poll string
|
||||
panicOnWarning bool
|
||||
forceSyncStatic bool
|
||||
printPathWarnings bool
|
||||
printUnusedTemplates bool
|
||||
baseURL string
|
||||
gc bool
|
||||
poll string
|
||||
forceSyncStatic bool
|
||||
|
||||
// Profile flags (for debugging of performance problems)
|
||||
cpuprofile string
|
||||
@@ -127,17 +122,20 @@ type rootCommand struct {
|
||||
traceprofile string
|
||||
printm bool
|
||||
|
||||
// TODO(bep) var vs string
|
||||
logging bool
|
||||
verbose bool
|
||||
verboseLog bool
|
||||
debug bool
|
||||
quiet bool
|
||||
logLevel string
|
||||
|
||||
verbose bool
|
||||
debug bool
|
||||
quiet bool
|
||||
|
||||
renderToMemory bool
|
||||
|
||||
cfgFile string
|
||||
cfgDir string
|
||||
logFile string
|
||||
}
|
||||
|
||||
func (r *rootCommand) isVerbose() bool {
|
||||
return r.logger.Level() <= logg.LevelInfo
|
||||
}
|
||||
|
||||
func (r *rootCommand) Build(cd *simplecobra.Commandeer, bcfg hugolib.BuildCfg, cfg config.Provider) (*hugolib.HugoSites, error) {
|
||||
@@ -175,7 +173,7 @@ func (r *rootCommand) ConfigFromConfig(key int32, oldConf *commonConfig) (*commo
|
||||
|
||||
if !configs.Base.C.Clock.IsZero() {
|
||||
// TODO(bep) find a better place for this.
|
||||
htime.Clock = clock.Start(configs.Base.C.Clock)
|
||||
htime.Clock = clocks.Start(configs.Base.C.Clock)
|
||||
}
|
||||
|
||||
return &commonConfig{
|
||||
@@ -242,11 +240,11 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
|
||||
renderStaticToDisk := cfg.GetBool("renderStaticToDisk")
|
||||
|
||||
sourceFs := hugofs.Os
|
||||
var desinationFs afero.Fs
|
||||
var destinationFs afero.Fs
|
||||
if cfg.GetBool("renderToDisk") {
|
||||
desinationFs = hugofs.Os
|
||||
destinationFs = hugofs.Os
|
||||
} else {
|
||||
desinationFs = afero.NewMemMapFs()
|
||||
destinationFs = afero.NewMemMapFs()
|
||||
if renderStaticToDisk {
|
||||
// Hybrid, render dynamic content to Root.
|
||||
cfg.Set("publishDirDynamic", "/")
|
||||
@@ -257,7 +255,7 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
|
||||
}
|
||||
}
|
||||
|
||||
fs := hugofs.NewFromSourceAndDestination(sourceFs, desinationFs, cfg)
|
||||
fs := hugofs.NewFromSourceAndDestination(sourceFs, destinationFs, cfg)
|
||||
|
||||
if renderStaticToDisk {
|
||||
dynamicFs := fs.PublishDir
|
||||
@@ -285,10 +283,10 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
|
||||
|
||||
if !base.C.Clock.IsZero() {
|
||||
// TODO(bep) find a better place for this.
|
||||
htime.Clock = clock.Start(configs.Base.C.Clock)
|
||||
htime.Clock = clocks.Start(configs.Base.C.Clock)
|
||||
}
|
||||
|
||||
if base.LogPathWarnings {
|
||||
if base.PrintPathWarnings {
|
||||
// Note that we only care about the "dynamic creates" here,
|
||||
// so skip the static fs.
|
||||
fs.PublishDir = hugofs.NewCreateCountingFs(fs.PublishDir)
|
||||
@@ -310,7 +308,7 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
|
||||
|
||||
func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, error) {
|
||||
h, _, err := r.hugoSites.GetOrCreate(r.configVersionID.Load(), func(key int32) (*hugolib.HugoSites, error) {
|
||||
depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, Logger: r.logger}
|
||||
depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, LogOut: r.logger.Out(), LogLevel: r.logger.Level()}
|
||||
return hugolib.NewHugoSites(depsCfg)
|
||||
})
|
||||
return h, err
|
||||
@@ -322,7 +320,7 @@ func (r *rootCommand) Hugo(cfg config.Provider) (*hugolib.HugoSites, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, Logger: r.logger}
|
||||
depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, LogOut: r.logger.Out(), LogLevel: r.logger.Level()}
|
||||
return hugolib.NewHugoSites(depsCfg)
|
||||
})
|
||||
return h, err
|
||||
@@ -339,7 +337,7 @@ func (r *rootCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args
|
||||
|
||||
b := newHugoBuilder(r, nil)
|
||||
|
||||
if err := b.loadConfig(cd, true); err != nil {
|
||||
if err := b.loadConfig(cd, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -392,6 +390,9 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
if r.quiet {
|
||||
r.Out = io.Discard
|
||||
}
|
||||
// Used by mkcert (server).
|
||||
log.SetOutput(r.Out)
|
||||
|
||||
r.Printf = func(format string, v ...interface{}) {
|
||||
if !r.quiet {
|
||||
fmt.Fprintf(r.Out, format, v...)
|
||||
@@ -408,14 +409,7 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch r.format {
|
||||
case "json", "toml", "yaml":
|
||||
// OK
|
||||
default:
|
||||
return fmt.Errorf("unsupported format %q; must be one of json, toml or yaml", r.format)
|
||||
}
|
||||
|
||||
loggers.PanicOnWarning.Store(r.panicOnWarning)
|
||||
r.commonConfigs = lazycache.New[int32, *commonConfig](lazycache.Options{MaxEntries: 5})
|
||||
r.hugoSites = lazycache.New[int32, *hugolib.HugoSites](lazycache.Options{MaxEntries: 5})
|
||||
|
||||
@@ -423,48 +417,48 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
}
|
||||
|
||||
func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
|
||||
var (
|
||||
logHandle = io.Discard
|
||||
logThreshold = jww.LevelWarn
|
||||
outHandle = r.Out
|
||||
stdoutThreshold = jww.LevelWarn
|
||||
)
|
||||
level := logg.LevelWarn
|
||||
|
||||
if r.verboseLog || r.logging || (r.logFile != "") {
|
||||
var err error
|
||||
if r.logFile != "" {
|
||||
logHandle, err = os.OpenFile(r.logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to open log file %q: %s", r.logFile, err)
|
||||
}
|
||||
} else {
|
||||
logHandle, err = os.CreateTemp("", "hugo")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.logLevel != "" {
|
||||
switch strings.ToLower(r.logLevel) {
|
||||
case "debug":
|
||||
level = logg.LevelDebug
|
||||
case "info":
|
||||
level = logg.LevelInfo
|
||||
case "warn", "warning":
|
||||
level = logg.LevelWarn
|
||||
case "error":
|
||||
level = logg.LevelError
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid log level: %q, must be one of debug, warn, info or error", r.logLevel)
|
||||
}
|
||||
} else {
|
||||
if r.verbose {
|
||||
helpers.Deprecated("--verbose", "use --logLevel info", false)
|
||||
level = logg.LevelInfo
|
||||
}
|
||||
} else if r.verbose {
|
||||
stdoutThreshold = jww.LevelInfo
|
||||
}
|
||||
|
||||
if r.debug {
|
||||
stdoutThreshold = jww.LevelDebug
|
||||
}
|
||||
|
||||
if r.verboseLog {
|
||||
logThreshold = jww.LevelInfo
|
||||
if r.debug {
|
||||
logThreshold = jww.LevelDebug
|
||||
helpers.Deprecated("--debug", "use --logLevel debug", false)
|
||||
level = logg.LevelDebug
|
||||
}
|
||||
}
|
||||
|
||||
loggers.InitGlobalLogger(stdoutThreshold, logThreshold, outHandle, logHandle)
|
||||
helpers.InitLoggers()
|
||||
return loggers.NewLogger(stdoutThreshold, logThreshold, outHandle, logHandle, running), nil
|
||||
optsLogger := loggers.Options{
|
||||
Distinct: true,
|
||||
Level: level,
|
||||
Stdout: r.Out,
|
||||
Stderr: r.Out,
|
||||
StoreErrors: running,
|
||||
}
|
||||
|
||||
return loggers.New(optsLogger), nil
|
||||
|
||||
}
|
||||
|
||||
func (r *rootCommand) Reset() {
|
||||
r.logger.Reset()
|
||||
loggers.Log().Reset()
|
||||
}
|
||||
|
||||
// IsTestRun reports whether the command is running as a test.
|
||||
@@ -485,7 +479,6 @@ Complete documentation is available at https://gohugo.io/.`
|
||||
|
||||
// Configure persistent flags
|
||||
cmd.PersistentFlags().StringVarP(&r.source, "source", "s", "", "filesystem path to read files relative from")
|
||||
cmd.PersistentFlags().StringVar(&r.format, "format", "toml", "preferred file format (toml, yaml or json)")
|
||||
cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
cmd.PersistentFlags().StringP("destination", "d", "", "filesystem path to write files to")
|
||||
cmd.PersistentFlags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
|
||||
@@ -504,17 +497,12 @@ Complete documentation is available at https://gohugo.io/.`
|
||||
|
||||
cmd.PersistentFlags().BoolVarP(&r.verbose, "verbose", "v", false, "verbose output")
|
||||
cmd.PersistentFlags().BoolVarP(&r.debug, "debug", "", false, "debug output")
|
||||
cmd.PersistentFlags().BoolVar(&r.logging, "log", false, "enable Logging")
|
||||
cmd.PersistentFlags().StringVar(&r.logFile, "logFile", "", "log File path (if set, logging enabled automatically)")
|
||||
cmd.PersistentFlags().BoolVar(&r.verboseLog, "verboseLog", false, "verbose logging")
|
||||
cmd.PersistentFlags().StringVar(&r.logLevel, "logLevel", "", "log level (debug|info|warn|error)")
|
||||
cmd.Flags().BoolVarP(&r.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
|
||||
cmd.Flags().BoolVar(&r.renderToMemory, "renderToMemory", false, "render to memory (only useful for benchmark testing)")
|
||||
|
||||
// Set bash-completion
|
||||
_ = cmd.PersistentFlags().SetAnnotation("logFile", cobra.BashCompFilenameExt, []string{})
|
||||
|
||||
// Configure local flags
|
||||
applyLocalBuildFlags(cmd, r)
|
||||
applyLocalFlagsBuild(cmd, r)
|
||||
|
||||
// Set bash-completion.
|
||||
// Each flag must first be defined before using the SetAnnotation() call.
|
||||
@@ -523,21 +511,30 @@ Complete documentation is available at https://gohugo.io/.`
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyLocalBuildFlags(cmd *cobra.Command, r *rootCommand) {
|
||||
// A sub set of the complete build flags. These flags are used by new and mod.
|
||||
func applyLocalFlagsBuildConfig(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)")
|
||||
cmd.Flags().StringVarP(&r.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. https://spf13.com/")
|
||||
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/")
|
||||
_ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
|
||||
cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory")
|
||||
_ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
|
||||
|
||||
}
|
||||
|
||||
// Flags needed to do a build (used by hugo and hugo server commands)
|
||||
func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) {
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
cmd.Flags().Bool("cleanDestinationDir", false, "remove files from destination not found in static directories")
|
||||
cmd.Flags().BoolP("buildDrafts", "D", false, "include content marked as draft")
|
||||
cmd.Flags().BoolP("buildFuture", "F", false, "include content with publishdate in the future")
|
||||
cmd.Flags().BoolP("buildExpired", "E", false, "include expired content")
|
||||
cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory")
|
||||
cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory")
|
||||
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/")
|
||||
cmd.Flags().BoolP("ignoreCache", "", false, "ignores the cache directory")
|
||||
cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)")
|
||||
cmd.Flags().StringVarP(&r.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. https://spf13.com/")
|
||||
cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date, author, and CODEOWNERS info to the pages")
|
||||
cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory")
|
||||
cmd.Flags().BoolVar(&r.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build")
|
||||
cmd.Flags().StringVar(&r.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(&r.panicOnWarning, "panicOnWarning", false, "panic on first WARNING log")
|
||||
cmd.Flags().Bool("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().BoolVar(&r.forceSyncStatic, "forceSyncStatic", false, "copy all files when static is changed.")
|
||||
@@ -545,8 +542,8 @@ func applyLocalBuildFlags(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files")
|
||||
cmd.Flags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file")
|
||||
cmd.Flags().BoolP("printI18nWarnings", "", false, "print missing translations")
|
||||
cmd.Flags().BoolVarP(&r.printPathWarnings, "printPathWarnings", "", false, "print warnings on duplicate target paths etc.")
|
||||
cmd.Flags().BoolVarP(&r.printUnusedTemplates, "printUnusedTemplates", "", false, "print warnings on unused templates.")
|
||||
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(&r.cpuprofile, "profile-cpu", "", "", "write cpu profile to `file`")
|
||||
cmd.Flags().StringVarP(&r.memprofile, "profile-mem", "", "", "write memory profile to `file`")
|
||||
cmd.Flags().BoolVarP(&r.printm, "printMemoryUsage", "", false, "print memory usage to screen at intervals")
|
||||
@@ -559,12 +556,8 @@ func applyLocalBuildFlags(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().MarkHidden("profile-mutex")
|
||||
|
||||
cmd.Flags().StringSlice("disableKinds", []string{}, "disable different kind of pages (home, RSS etc.)")
|
||||
|
||||
cmd.Flags().Bool("minify", false, "minify any supported output format (HTML, XML etc.)")
|
||||
|
||||
_ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.Flags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
|
||||
|
||||
}
|
||||
|
||||
@@ -579,7 +572,7 @@ type simpleCommand struct {
|
||||
short string
|
||||
long string
|
||||
run func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *rootCommand, args []string) error
|
||||
withc func(cmd *cobra.Command)
|
||||
withc func(cmd *cobra.Command, r *rootCommand)
|
||||
initc func(cd *simplecobra.Commandeer) error
|
||||
|
||||
commands []simplecobra.Commander
|
||||
@@ -603,6 +596,7 @@ func (c *simpleCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
}
|
||||
|
||||
func (c *simpleCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
c.rootCmd = cd.Root.Command.(*rootCommand)
|
||||
cmd := cd.CobraCommand
|
||||
cmd.Short = c.short
|
||||
cmd.Long = c.long
|
||||
@@ -610,13 +604,12 @@ func (c *simpleCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
cmd.Use = c.use
|
||||
}
|
||||
if c.withc != nil {
|
||||
c.withc(cmd)
|
||||
c.withc(cmd, c.rootCmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *simpleCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
c.rootCmd = cd.Root.Command.(*rootCommand)
|
||||
if c.initc != nil {
|
||||
return c.initc(cd)
|
||||
}
|
||||
|
||||
+27
-4
@@ -17,11 +17,13 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
@@ -40,6 +42,9 @@ func newConfigCommand() *configCommand {
|
||||
type configCommand struct {
|
||||
r *rootCommand
|
||||
|
||||
format string
|
||||
lang string
|
||||
|
||||
commands []simplecobra.Commander
|
||||
}
|
||||
|
||||
@@ -56,7 +61,16 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config := conf.configs.Base
|
||||
var config *allconfig.Config
|
||||
if c.lang != "" {
|
||||
var found bool
|
||||
config, found = conf.configs.LanguageConfigMap[c.lang]
|
||||
if !found {
|
||||
return fmt.Errorf("language %q not found", c.lang)
|
||||
}
|
||||
} else {
|
||||
config = conf.configs.LanguageConfigSlice[0]
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
dec := json.NewEncoder(&buf)
|
||||
@@ -67,7 +81,7 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
return err
|
||||
}
|
||||
|
||||
format := strings.ToLower(c.r.format)
|
||||
format := strings.ToLower(c.format)
|
||||
|
||||
switch format {
|
||||
case "json":
|
||||
@@ -83,6 +97,8 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
return parser.InterfaceToConfig(m, metadecoders.YAML, os.Stdout)
|
||||
case "toml":
|
||||
return parser.InterfaceToConfig(m, metadecoders.TOML, os.Stdout)
|
||||
default:
|
||||
return fmt.Errorf("unsupported format: %q", format)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,14 +106,18 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
}
|
||||
|
||||
func (c *configCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
c.r = cd.Root.Command.(*rootCommand)
|
||||
cmd := cd.CobraCommand
|
||||
cmd.Short = "Print the site configuration"
|
||||
cmd.Long = `Print the site configuration, both default and custom settings.`
|
||||
cmd.Flags().StringVar(&c.format, "format", "toml", "preferred file format (toml, yaml or json)")
|
||||
cmd.Flags().StringVar(&c.lang, "lang", "", "the language to display config for. Defaults to the first language defined.")
|
||||
applyLocalFlagsBuildConfig(cmd, c.r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *configCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
c.r = cd.Root.Command.(*rootCommand)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -172,6 +192,7 @@ func (m *configModMounts) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
type configMountsCommand struct {
|
||||
r *rootCommand
|
||||
configCmd *configCommand
|
||||
}
|
||||
|
||||
@@ -191,7 +212,7 @@ func (c *configMountsCommand) Run(ctx context.Context, cd *simplecobra.Commandee
|
||||
}
|
||||
|
||||
for _, m := range conf.configs.Modules {
|
||||
if err := parser.InterfaceToConfig(&configModMounts{m: m, verbose: r.verbose}, metadecoders.JSON, os.Stdout); err != nil {
|
||||
if err := parser.InterfaceToConfig(&configModMounts{m: m, verbose: r.isVerbose()}, metadecoders.JSON, os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -199,8 +220,10 @@ func (c *configMountsCommand) Run(ctx context.Context, cd *simplecobra.Commandee
|
||||
}
|
||||
|
||||
func (c *configMountsCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
c.r = cd.Root.Command.(*rootCommand)
|
||||
cmd := cd.CobraCommand
|
||||
cmd.Short = "Print the configured file mounts"
|
||||
applyLocalFlagsBuildConfig(cmd, c.r)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -45,7 +45,7 @@ to use JSON for the front matter.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
return c.convertContents(metadecoders.JSON)
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
@@ -56,7 +56,7 @@ to use TOML for the front matter.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
return c.convertContents(metadecoders.TOML)
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
@@ -67,7 +67,7 @@ to use YAML for the front matter.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
return c.convertContents(metadecoders.YAML)
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -84,7 +84,7 @@ type convertCommand struct {
|
||||
r *rootCommand
|
||||
h *hugolib.HugoSites
|
||||
|
||||
// Commmands.
|
||||
// Commands.
|
||||
commands []simplecobra.Commander
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ See convert's subcommands toJSON, toTOML and toYAML for more information.`
|
||||
cmd.PersistentFlags().StringVarP(&c.outputDir, "output", "o", "", "filesystem path to write files to")
|
||||
cmd.PersistentFlags().BoolVar(&c.unsafe, "unsafe", false, "enable less safe operations, please backup first")
|
||||
|
||||
cmd.RunE = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -140,7 +141,7 @@ func (c *convertCommand) convertAndSavePage(p page.Page, site *hugolib.Site, tar
|
||||
|
||||
errMsg := fmt.Errorf("error processing file %q", p.File().Path())
|
||||
|
||||
site.Log.Infoln("ttempting to convert", p.File().Filename())
|
||||
site.Log.Infoln("attempting to convert", p.File().Filename())
|
||||
|
||||
f := p.File()
|
||||
file, err := f.FileInfo().Meta().Open()
|
||||
|
||||
+5
-5
@@ -52,20 +52,20 @@ documentation.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deployer, err := deploy.New(h.Configs.GetFirstLanguageConfig(), h.PathSpec.PublishFs)
|
||||
deployer, err := deploy.New(h.Configs.GetFirstLanguageConfig(), h.Log, h.PathSpec.PublishFs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return deployer.Deploy(ctx)
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one")
|
||||
cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target")
|
||||
cmd.Flags().Bool("dryRun", false, "dry run")
|
||||
cmd.Flags().Bool("force", false, "force upload of all files")
|
||||
cmd.Flags().Bool("invalidateCDN", true, "invalidate the CDN cache listed in the deployment target")
|
||||
cmd.Flags().Int("maxDeletes", 256, "maximum # of files to delete, or -1 to disable")
|
||||
cmd.Flags().Int("workers", 10, "number of workers to transfer files. defaults to 10")
|
||||
cmd.Flags().Bool("invalidateCDN", deploy.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target")
|
||||
cmd.Flags().Int("maxDeletes", deploy.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable")
|
||||
cmd.Flags().Int("workers", deploy.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -41,7 +42,7 @@ func newDeployCommand() simplecobra.Commander {
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Hidden = true
|
||||
},
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ func newEnvCommand() simplecobra.Commander {
|
||||
r.Printf("GOARCH=%q\n", runtime.GOARCH)
|
||||
r.Printf("GOVERSION=%q\n", runtime.Version())
|
||||
|
||||
if r.verbose {
|
||||
if r.isVerbose() {
|
||||
deps := hugo.GetDependencyList()
|
||||
for _, dep := range deps {
|
||||
r.Printf("%s\n", dep)
|
||||
|
||||
+6
-4
@@ -70,7 +70,7 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
|
||||
formatter.WriteCSS(os.Stdout, style)
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.PersistentFlags().StringVar(&style, "style", "friendly", "highlighter style (see https://xyproto.github.io/splash/docs/)")
|
||||
cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "bg:#ffffcc", "style used for highlighting lines (see https://github.com/alecthomas/chroma)")
|
||||
cmd.PersistentFlags().StringVar(&linesStyle, "linesStyle", "", "style used for line numbers (see https://github.com/alecthomas/chroma)")
|
||||
@@ -110,7 +110,7 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
|
||||
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.PersistentFlags().StringVar(&genmandir, "dir", "man/", "the directory to write the man pages.")
|
||||
// For bash-completion
|
||||
cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
|
||||
@@ -167,7 +167,7 @@ url: %s
|
||||
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.PersistentFlags().StringVar(&gendocdir, "dir", "/tmp/hugodoc/", "the directory to write the doc.")
|
||||
// For bash-completion
|
||||
cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
|
||||
@@ -204,7 +204,7 @@ url: %s
|
||||
r.Println("Done!")
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Hidden = true
|
||||
cmd.PersistentFlags().StringVarP(&docsHelperTarget, "dir", "", "docs/data", "data dir")
|
||||
},
|
||||
@@ -243,6 +243,8 @@ func (c *genCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args [
|
||||
func (c *genCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
cmd := cd.CobraCommand
|
||||
cmd.Short = "A collection of several useful generators."
|
||||
|
||||
cmd.RunE = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -79,11 +79,9 @@ func flagsToCfgWithAdditionalConfigBase(cd *simplecobra.Commandeer, cfg config.P
|
||||
|
||||
// Flags with a different name in the config.
|
||||
keyMap := map[string]string{
|
||||
"minify": "minifyOutput",
|
||||
"destination": "publishDir",
|
||||
"printI18nWarnings": "logI18nWarnings",
|
||||
"printPathWarnings": "logPathWarnings",
|
||||
"editor": "newContentEditor",
|
||||
"minify": "minifyOutput",
|
||||
"destination": "publishDir",
|
||||
"editor": "newContentEditor",
|
||||
}
|
||||
|
||||
// Flags that we for some reason don't want to expose in the site config.
|
||||
|
||||
+37
-39
@@ -26,11 +26,13 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/common/htime"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/terminal"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
@@ -41,9 +43,7 @@ import (
|
||||
"github.com/gohugoio/hugo/hugolib/filesystems"
|
||||
"github.com/gohugoio/hugo/livereload"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
"github.com/gohugoio/hugo/watcher"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/fsync"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/semaphore"
|
||||
@@ -68,7 +68,6 @@ type hugoBuilder struct {
|
||||
onConfigLoaded func(reloaded bool) error
|
||||
|
||||
fastRenderMode bool
|
||||
buildWatch bool
|
||||
showErrorInBrowser bool
|
||||
|
||||
errState hugoBuilderErrState
|
||||
@@ -131,7 +130,7 @@ func (e *hugoBuilderErrState) wasErr() bool {
|
||||
}
|
||||
|
||||
func (c *hugoBuilder) errCount() int {
|
||||
return int(c.r.logger.LogCounters().ErrorCounter.Count())
|
||||
return c.r.logger.LoggCount(logg.LevelError) + loggers.Log().LoggCount(logg.LevelError)
|
||||
}
|
||||
|
||||
// getDirList provides NewWatcher() with a list of directories to watch for changes.
|
||||
@@ -363,7 +362,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
|
||||
configFiles = conf.configs.LoadingInfo.ConfigFiles
|
||||
})
|
||||
|
||||
c.r.logger.Println("Watching for config changes in", strings.Join(configFiles, ", "))
|
||||
c.r.Println("Watching for config changes in", strings.Join(configFiles, ", "))
|
||||
for _, configFile := range configFiles {
|
||||
watcher.Add(configFile)
|
||||
configSet[configFile] = true
|
||||
@@ -418,25 +417,6 @@ func (c *hugoBuilder) build() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.r.printPathWarnings {
|
||||
hugofs.WalkFilesystems(h.Fs.PublishDir, func(fs afero.Fs) bool {
|
||||
if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
|
||||
dupes := dfs.ReportDuplicates()
|
||||
if dupes != "" {
|
||||
c.r.logger.Warnln("Duplicate target paths:", dupes)
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
if c.r.printUnusedTemplates {
|
||||
unusedTemplates := h.Tmpl().(tpl.UnusedTemplatesProvider).UnusedTemplates()
|
||||
for _, unusedTemplate := range unusedTemplates {
|
||||
c.r.logger.Warnf("Template %s is unused, source file %s", unusedTemplate.Name(), unusedTemplate.Filename())
|
||||
}
|
||||
}
|
||||
|
||||
h.PrintProcessingStats(os.Stdout)
|
||||
c.r.Println()
|
||||
}
|
||||
@@ -461,6 +441,7 @@ func (c *hugoBuilder) copyStatic() (map[string]uint64, error) {
|
||||
}
|
||||
|
||||
func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
|
||||
infol := c.r.logger.InfoCommand("copy static")
|
||||
publishDir := helpers.FilePathSeparator
|
||||
|
||||
if sourceFs.PublishFolder != "" {
|
||||
@@ -484,13 +465,13 @@ func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint
|
||||
syncer.SrcFs = fs
|
||||
|
||||
if syncer.Delete {
|
||||
c.r.logger.Infoln("removing all files from destination that don't exist in static dirs")
|
||||
infol.Logf("removing all files from destination that don't exist in static dirs")
|
||||
|
||||
syncer.DeleteFilter = func(f os.FileInfo) bool {
|
||||
return f.IsDir() && strings.HasPrefix(f.Name(), ".")
|
||||
}
|
||||
}
|
||||
c.r.logger.Infoln("syncing static files to", publishDir)
|
||||
infol.Logf("syncing static files to %s", publishDir)
|
||||
|
||||
// because we are using a baseFs (to get the union right).
|
||||
// set sync src to root
|
||||
@@ -545,14 +526,13 @@ func (c *hugoBuilder) fullBuild(noBuildLock bool) error {
|
||||
langCount map[string]uint64
|
||||
)
|
||||
|
||||
if !c.r.quiet {
|
||||
fmt.Println("Start building sites … ")
|
||||
fmt.Println(hugo.BuildVersionString())
|
||||
if terminal.IsTerminal(os.Stdout) {
|
||||
defer func() {
|
||||
fmt.Print(showCursor + clearLine)
|
||||
}()
|
||||
}
|
||||
c.r.logger.Println("Start building sites … ")
|
||||
c.r.logger.Println(hugo.BuildVersionString())
|
||||
c.r.logger.Println()
|
||||
if terminal.IsTerminal(os.Stdout) {
|
||||
defer func() {
|
||||
fmt.Print(showCursor + clearLine)
|
||||
}()
|
||||
}
|
||||
|
||||
copyStaticFunc := func() error {
|
||||
@@ -990,14 +970,28 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error
|
||||
cfg := config.New()
|
||||
cfg.Set("renderToDisk", (c.s == nil && !c.r.renderToMemory) || (c.s != nil && c.s.renderToDisk))
|
||||
watch := c.r.buildWatch || (c.s != nil && c.s.serverWatch)
|
||||
if c.r.environment != "" {
|
||||
cfg.Set("environment", c.r.environment)
|
||||
if c.r.environment == "" {
|
||||
// We need to set the environment as early as possible because we need it to load the correct config.
|
||||
// Check if the user has set it in env.
|
||||
if env := os.Getenv("HUGO_ENVIRONMENT"); env != "" {
|
||||
c.r.environment = env
|
||||
} else if env := os.Getenv("HUGO_ENV"); env != "" {
|
||||
c.r.environment = env
|
||||
} else {
|
||||
if c.s != nil {
|
||||
// The server defaults to development.
|
||||
c.r.environment = hugo.EnvironmentDevelopment
|
||||
} else {
|
||||
c.r.environment = hugo.EnvironmentProduction
|
||||
}
|
||||
}
|
||||
}
|
||||
cfg.Set("environment", c.r.environment)
|
||||
|
||||
cfg.Set("internal", maps.Params{
|
||||
"running": running,
|
||||
"watch": watch,
|
||||
"verbose": c.r.verbose,
|
||||
"verbose": c.r.isVerbose(),
|
||||
})
|
||||
|
||||
conf, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), flagsToCfg(cd, cfg))
|
||||
@@ -1005,6 +999,10 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error
|
||||
return err
|
||||
}
|
||||
|
||||
if len(conf.configs.LoadingInfo.ConfigFiles) == 0 {
|
||||
return errors.New("Unable to locate config file or config directory. Perhaps you need to create a new site.\nRun `hugo help new` for details.")
|
||||
}
|
||||
|
||||
c.conf = conf
|
||||
if c.onConfigLoaded != nil {
|
||||
if err := c.onConfigLoaded(false); err != nil {
|
||||
@@ -1044,8 +1042,8 @@ func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) error {
|
||||
if c.fastRenderMode {
|
||||
c.withConf(func(conf *commonConfig) {
|
||||
// Make sure we always render the home pages
|
||||
for _, l := range conf.configs.Languages {
|
||||
langPath := h.GetLangSubDir(l.Lang)
|
||||
for _, l := range conf.configs.ConfigLangs() {
|
||||
langPath := l.LanguagePrefix()
|
||||
if langPath != "" {
|
||||
langPath = langPath + "/"
|
||||
}
|
||||
|
||||
+13
-10
@@ -19,12 +19,11 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -59,7 +58,7 @@ Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root
|
||||
}
|
||||
return c.importFromJekyll(args)
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().BoolVar(&c.force, "force", false, "allow import into non-empty target directory")
|
||||
},
|
||||
},
|
||||
@@ -97,6 +96,7 @@ func (c *importCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
|
||||
Import requires a subcommand, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`."
|
||||
|
||||
cmd.RunE = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ func (c *importCommand) convertJekyllMetaData(m any, postName string, postDate t
|
||||
}
|
||||
|
||||
func (c *importCommand) convertJekyllPost(path, relPath, targetDir string, draft bool) error {
|
||||
jww.TRACE.Println("Converting", path)
|
||||
log.Println("Converting", path)
|
||||
|
||||
filename := filepath.Base(path)
|
||||
postDate, postName, err := c.parseJekyllFilename(filename)
|
||||
@@ -308,7 +308,7 @@ func (c *importCommand) convertJekyllPost(path, relPath, targetDir string, draft
|
||||
return nil
|
||||
}
|
||||
|
||||
jww.TRACE.Println(filename, postDate, postName)
|
||||
log.Println(filename, postDate, postName)
|
||||
|
||||
targetFile := filepath.Join(targetDir, relPath)
|
||||
targetParentDir := filepath.Dir(targetFile)
|
||||
@@ -367,7 +367,7 @@ func (c *importCommand) copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyl
|
||||
if _, ok := jekyllPostDirs[entry.Name()]; !ok {
|
||||
err = hugio.CopyDir(fs, sfp, dfp, nil)
|
||||
if err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
c.r.logger.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -388,7 +388,7 @@ func (c *importCommand) copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyl
|
||||
if !isExcept && entry.Name()[0] != '.' && entry.Name()[0] != '_' {
|
||||
err = hugio.CopyFile(fs, sfp, dfp)
|
||||
if err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
c.r.logger.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -469,9 +469,12 @@ func (c *importCommand) importFromJekyll(args []string) error {
|
||||
}
|
||||
|
||||
c.r.Println("Congratulations!", fileCount, "post(s) imported!")
|
||||
c.r.Println("Now, start Hugo by yourself:\n" +
|
||||
"$ git clone https://github.com/spf13/herring-cove.git " + args[1] + "/themes/herring-cove")
|
||||
c.r.Println("$ cd " + args[1] + "\n$ hugo server --theme=herring-cove")
|
||||
c.r.Println("Now, start Hugo by yourself:\n")
|
||||
c.r.Println("cd " + args[1])
|
||||
c.r.Println("git init")
|
||||
c.r.Println("git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke themes/ananke")
|
||||
c.r.Println("echo \"theme = 'ananke'\" > hugo.toml")
|
||||
c.r.Println("hugo server")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ func (c *listCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
|
||||
List requires a subcommand, e.g. hugo list drafts`
|
||||
|
||||
cmd.RunE = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+20
-5
@@ -61,6 +61,9 @@ This command is marked as 'Experimental'. We think it's a great idea, so it's no
|
||||
removed from Hugo, but we need to test this out in "real life" to get a feel of it,
|
||||
so this may/will change in future versions of Hugo.
|
||||
`,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
h, err := r.Hugo(flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
@@ -85,6 +88,9 @@ so this may/will change in future versions of Hugo.
|
||||
Note that Hugo Modules supports multi-module projects, so you can initialize a Hugo Module
|
||||
inside a subfolder on GitHub, as one example.
|
||||
`,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
h, err := r.Hugo(flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
@@ -101,7 +107,8 @@ so this may/will change in future versions of Hugo.
|
||||
name: "verify",
|
||||
short: "Verify dependencies.",
|
||||
long: `Verify checks that the dependencies of the current module, which are stored in a local downloaded source cache, have not been modified since being downloaded.`,
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification")
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
@@ -119,7 +126,8 @@ so this may/will change in future versions of Hugo.
|
||||
long: `Print a module dependency graph with information about module status (disabled, vendored).
|
||||
Note that for vendored modules, that is the version listed and not the one from go.mod.
|
||||
`,
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification")
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
@@ -135,7 +143,8 @@ Note that for vendored modules, that is the version listed and not the one from
|
||||
name: "clean",
|
||||
short: "Delete the Hugo Module cache for the current project.",
|
||||
long: `Delete the Hugo Module cache for the current project.`,
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
cmd.Flags().StringVarP(&pattern, "pattern", "", "", `pattern matching module paths to clean (all if not set), e.g. "**hugo*"`)
|
||||
cmd.Flags().BoolVarP(&all, "all", "", false, "clean entire module cache")
|
||||
},
|
||||
@@ -157,6 +166,9 @@ Note that for vendored modules, that is the version listed and not the one from
|
||||
&simpleCommand{
|
||||
name: "tidy",
|
||||
short: "Remove unused entries in go.mod and go.sum.",
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
h, err := r.Hugo(flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
@@ -171,6 +183,9 @@ Note that for vendored modules, that is the version listed and not the one from
|
||||
long: `Vendor all module dependencies into the _vendor directory.
|
||||
If a module is vendored, that is where Hugo will look for it's dependencies.
|
||||
`,
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
h, err := r.Hugo(flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
@@ -203,13 +218,13 @@ Install the latest versions of all module dependencies:
|
||||
|
||||
Run "go help get" for more information. All flags available for "go get" is also relevant here.
|
||||
` + commonUsageMod,
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.DisableFlagParsing = true
|
||||
},
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
// We currently just pass on the flags we get to Go and
|
||||
// need to do the flag handling manually.
|
||||
if len(args) == 1 && args[0] == "-h" {
|
||||
if len(args) == 1 && (args[0] == "-h" || args[0] == "--help") {
|
||||
return errHelp
|
||||
}
|
||||
|
||||
|
||||
+31
-20
@@ -36,6 +36,7 @@ func newNewCommand() *newCommand {
|
||||
var (
|
||||
force bool
|
||||
contentType string
|
||||
format string
|
||||
)
|
||||
|
||||
var c *newCommand
|
||||
@@ -46,16 +47,16 @@ func newNewCommand() *newCommand {
|
||||
use: "content [path]",
|
||||
short: "Create new content for your site",
|
||||
long: `Create a new content file and automatically set the date and title.
|
||||
It will guess which kind of file to create based on the path provided.
|
||||
|
||||
You can also specify the kind with ` + "`-k KIND`" + `.
|
||||
|
||||
If archetypes are provided in your theme or site, they will be used.
|
||||
|
||||
Ensure you run this within the root directory of your site.`,
|
||||
It will guess which kind of file to create based on the path provided.
|
||||
|
||||
You can also specify the kind with ` + "`-k KIND`" + `.
|
||||
|
||||
If archetypes are provided in your theme or site, they will be used.
|
||||
|
||||
Ensure you run this within the root directory of your site.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return errors.New("path needs to be provided")
|
||||
return newUserError("path needs to be provided")
|
||||
}
|
||||
h, err := r.Hugo(flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
@@ -63,10 +64,13 @@ func newNewCommand() *newCommand {
|
||||
}
|
||||
return create.NewContent(h, contentType, args[0], force)
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().StringVarP(&contentType, "kind", "k", "", "content type to create")
|
||||
cmd.Flags().String("editor", "", "edit new content with this editor, if provided")
|
||||
cmd.Flags().BoolVarP(&force, "force", "f", false, "overwrite file if it already exists")
|
||||
cmd.Flags().StringVar(&format, "format", "toml", "preferred file format (toml, yaml or json)")
|
||||
applyLocalFlagsBuildConfig(cmd, r)
|
||||
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
@@ -78,7 +82,7 @@ The new site will have the correct structure, but no content or theme yet.
|
||||
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return errors.New("path needs to be provided")
|
||||
return newUserError("path needs to be provided")
|
||||
}
|
||||
createpath, err := filepath.Abs(filepath.Clean(args[0]))
|
||||
if err != nil {
|
||||
@@ -118,7 +122,7 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
return errors.New(createpath + " already exists and is not empty. See --force.")
|
||||
|
||||
case !isEmpty && force:
|
||||
all := append(dirs, filepath.Join(createpath, "hugo."+r.format))
|
||||
all := append(dirs, filepath.Join(createpath, "hugo."+format))
|
||||
for _, path := range all {
|
||||
if exists, _ := helpers.Exists(path, sourceFs); exists {
|
||||
return errors.New(path + " already exists")
|
||||
@@ -133,7 +137,7 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
}
|
||||
}
|
||||
|
||||
c.newSiteCreateConfig(sourceFs, createpath, r.format)
|
||||
c.newSiteCreateConfig(sourceFs, createpath, format)
|
||||
|
||||
// Create a default archetype file.
|
||||
helpers.SafeWriteToDisk(filepath.Join(archeTypePath, "default.md"),
|
||||
@@ -144,18 +148,23 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
cmd.Flags().BoolVar(&force, "force", false, "init inside non-empty directory")
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().BoolVarP(&force, "force", "f", false, "init inside non-empty directory")
|
||||
cmd.Flags().StringVar(&format, "format", "toml", "preferred file format (toml, yaml or json)")
|
||||
},
|
||||
},
|
||||
&simpleCommand{
|
||||
name: "theme",
|
||||
use: "theme [path]",
|
||||
short: "Create a new site (skeleton)",
|
||||
long: `Create a new site in the provided directory.
|
||||
The new site will have the correct structure, but no content or theme yet.
|
||||
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
use: "theme [name]",
|
||||
short: "Create a new theme (skeleton)",
|
||||
long: `Create a new theme (skeleton) called [name] in ./themes.
|
||||
New theme is a skeleton. Please add content to the touched files. Add your
|
||||
name to the copyright line in the license and adjust the theme.toml file
|
||||
according to your needs.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return newUserError("theme name needs to be provided")
|
||||
}
|
||||
h, err := r.Hugo(flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -280,6 +289,8 @@ You can also specify the kind with ` + "`-k KIND`" + `.
|
||||
If archetypes are provided in your theme or site, they will be used.
|
||||
|
||||
Ensure you run this within the root directory of your site.`
|
||||
|
||||
cmd.RunE = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -337,7 +348,7 @@ description = ""
|
||||
homepage = "http://example.com/"
|
||||
tags = []
|
||||
features = []
|
||||
min_version = "0.112.1"
|
||||
min_version = "0.115.0"
|
||||
|
||||
[author]
|
||||
name = ""
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ func newReleaseCommand() simplecobra.Commander {
|
||||
|
||||
return rel.Run()
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Hidden = true
|
||||
cmd.PersistentFlags().BoolVarP(&skipPush, "skip-push", "", false, "skip pushing to remote")
|
||||
cmd.PersistentFlags().BoolVarP(&try, "try", "", false, "no changes")
|
||||
|
||||
+165
-38
@@ -16,7 +16,10 @@ package commands
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -28,6 +31,8 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/bep/mclib"
|
||||
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -55,13 +60,13 @@ import (
|
||||
"github.com/gohugoio/hugo/transform"
|
||||
"github.com/gohugoio/hugo/transform/livereloadinject"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/fsync"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
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*`)
|
||||
)
|
||||
@@ -97,13 +102,38 @@ func newHugoBuilder(r *rootCommand, s *serverCommand, onConfigLoaded ...func(rel
|
||||
}
|
||||
|
||||
func newServerCommand() *serverCommand {
|
||||
var c *serverCommand
|
||||
c = &serverCommand{
|
||||
// Flags.
|
||||
var uninstall bool
|
||||
|
||||
c := &serverCommand{
|
||||
quit: make(chan bool),
|
||||
commands: []simplecobra.Commander{
|
||||
&simpleCommand{
|
||||
name: "trust",
|
||||
short: "Install the local CA in the system trust store.",
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
action := "-install"
|
||||
if uninstall {
|
||||
action = "-uninstall"
|
||||
}
|
||||
os.Args = []string{action}
|
||||
return mclib.RunMain()
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().BoolVar(&uninstall, "uninstall", false, "Uninstall the local CA (but do not delete it).")
|
||||
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *serverCommand) Commands() []simplecobra.Commander {
|
||||
return c.commands
|
||||
}
|
||||
|
||||
type countingStatFs struct {
|
||||
afero.Fs
|
||||
statCounter uint64
|
||||
@@ -204,7 +234,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
|
||||
listener := f.c.serverPorts[i].ln
|
||||
logger := f.c.r.logger
|
||||
|
||||
r.Printf("Environment: %q", f.c.hugoTry().Deps.Site.Hugo().Environment)
|
||||
r.Printf("Environment: %q\n", f.c.hugoTry().Deps.Site.Hugo().Environment)
|
||||
|
||||
if i == 0 {
|
||||
if f.c.renderToDisk {
|
||||
@@ -288,7 +318,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
|
||||
}
|
||||
var fs afero.Fs
|
||||
f.c.withConf(func(conf *commonConfig) {
|
||||
fs = conf.fs.PublishDir
|
||||
fs = conf.fs.PublishDirServer
|
||||
})
|
||||
|
||||
fi, err := fs.Stat(path)
|
||||
@@ -423,6 +453,9 @@ type serverCommand struct {
|
||||
navigateToChanged bool
|
||||
serverAppend bool
|
||||
serverInterface string
|
||||
tlsCertFile string
|
||||
tlsKeyFile string
|
||||
tlsAuto bool
|
||||
serverPort int
|
||||
liveReloadPort int
|
||||
serverWatch bool
|
||||
@@ -432,10 +465,6 @@ type serverCommand struct {
|
||||
disableBrowserError bool
|
||||
}
|
||||
|
||||
func (c *serverCommand) Commands() []simplecobra.Commander {
|
||||
return c.commands
|
||||
}
|
||||
|
||||
func (c *serverCommand) Name() string {
|
||||
return "server"
|
||||
}
|
||||
@@ -480,8 +509,6 @@ func (c *serverCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
cmd.Short = "A high performance webserver"
|
||||
cmd.Long = `Hugo provides its own webserver which builds and serves the site.
|
||||
While hugo server is high performance, it is a webserver with limited options.
|
||||
Many run it in production, but the standard behavior is for people to use it
|
||||
in development and use a more full featured server such as Nginx or Caddy.
|
||||
|
||||
'hugo server' will avoid writing the rendered and served content to disk,
|
||||
preferring to store it in memory.
|
||||
@@ -495,6 +522,9 @@ of a second, you will be able to save and see your changes nearly instantly.`
|
||||
cmd.Flags().IntVarP(&c.serverPort, "port", "p", 1313, "port on which the server will listen")
|
||||
cmd.Flags().IntVar(&c.liveReloadPort, "liveReloadPort", -1, "port for live reloading (i.e. 443 in HTTPS proxy situations)")
|
||||
cmd.Flags().StringVarP(&c.serverInterface, "bind", "", "127.0.0.1", "interface to which the server will bind")
|
||||
cmd.Flags().StringVarP(&c.tlsCertFile, "tlsCertFile", "", "", "path to TLS certificate file")
|
||||
cmd.Flags().StringVarP(&c.tlsKeyFile, "tlsKeyFile", "", "", "path to TLS key file")
|
||||
cmd.Flags().BoolVar(&c.tlsAuto, "tlsAuto", false, "generate and use locally-trusted certificates.")
|
||||
cmd.Flags().BoolVarP(&c.serverWatch, "watch", "w", true, "watch filesystem for changes and recreate as needed")
|
||||
cmd.Flags().BoolVar(&c.noHTTPCache, "noHTTPCache", false, "prevent HTTP caching")
|
||||
cmd.Flags().BoolVarP(&c.serverAppend, "appendPort", "", true, "append port to baseURL")
|
||||
@@ -508,8 +538,11 @@ of a second, you will be able to save and see your changes nearly instantly.`
|
||||
cmd.Flags().String("memstats", "", "log memory usage to this file")
|
||||
cmd.Flags().String("meminterval", "100ms", "interval to poll memory usage (requires --memstats), valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".")
|
||||
|
||||
cmd.Flags().SetAnnotation("tlsCertFile", cobra.BashCompSubdirsInDir, []string{})
|
||||
cmd.Flags().SetAnnotation("tlsKeyFile", cobra.BashCompSubdirsInDir, []string{})
|
||||
|
||||
r := cd.Root.Command.(*rootCommand)
|
||||
applyLocalBuildFlags(cmd, r)
|
||||
applyLocalFlagsBuild(cmd, r)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -525,7 +558,14 @@ func (c *serverCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
if err := c.createServerPorts(cd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if (c.tlsCertFile == "" || c.tlsKeyFile == "") && c.tlsAuto {
|
||||
c.withConfE(func(conf *commonConfig) error {
|
||||
return c.createCertificates(conf)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.setBaseURLsInConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -546,9 +586,6 @@ func (c *serverCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
c.doLiveReload = !c.disableLiveReload
|
||||
c.fastRenderMode = !c.disableFastRender
|
||||
c.showErrorInBrowser = c.doLiveReload && !c.disableBrowserError
|
||||
if c.r.environment == "" {
|
||||
c.r.environment = hugo.EnvironmentDevelopment
|
||||
}
|
||||
|
||||
if c.fastRenderMode {
|
||||
// For now, fast render mode only. It should, however, be fast enough
|
||||
@@ -614,8 +651,8 @@ func (c *serverCommand) getErrorWithContext() any {
|
||||
|
||||
m := make(map[string]any)
|
||||
|
||||
//xwm["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.r.logger.Errors())))
|
||||
m["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.r.logger.Errors())))
|
||||
m["Error"] = cleanErrorLog(c.r.logger.Errors())
|
||||
|
||||
m["Version"] = hugo.BuildVersionString()
|
||||
ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr())
|
||||
m["Files"] = ferrors
|
||||
@@ -623,6 +660,78 @@ func (c *serverCommand) getErrorWithContext() any {
|
||||
return m
|
||||
}
|
||||
|
||||
func (c *serverCommand) createCertificates(conf *commonConfig) error {
|
||||
hostname := "localhost"
|
||||
if c.r.baseURL != "" {
|
||||
u, err := url.Parse(c.r.baseURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostname = u.Hostname()
|
||||
}
|
||||
|
||||
// For now, store these in the Hugo cache dir.
|
||||
// Hugo should probably introduce some concept of a less temporary application directory.
|
||||
keyDir := filepath.Join(conf.configs.LoadingInfo.BaseConfig.CacheDir, "_mkcerts")
|
||||
|
||||
// Create the directory if it doesn't exist.
|
||||
if _, err := os.Stat(keyDir); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(keyDir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.tlsCertFile = filepath.Join(keyDir, fmt.Sprintf("%s.pem", hostname))
|
||||
c.tlsKeyFile = filepath.Join(keyDir, fmt.Sprintf("%s-key.pem", hostname))
|
||||
|
||||
// Check if the certificate already exists and is valid.
|
||||
certPEM, err := ioutil.ReadFile(c.tlsCertFile)
|
||||
if err == nil {
|
||||
rootPem, err := ioutil.ReadFile(filepath.Join(mclib.GetCAROOT(), "rootCA.pem"))
|
||||
if err == nil {
|
||||
if err := c.verifyCert(rootPem, certPEM, hostname); err == nil {
|
||||
c.r.Println("Using existing", c.tlsCertFile, "and", c.tlsKeyFile)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.r.Println("Creating TLS certificates in", keyDir)
|
||||
|
||||
// Yes, this is unfortunate, but it's currently the only way to use Mkcert as a library.
|
||||
os.Args = []string{"-cert-file", c.tlsCertFile, "-key-file", c.tlsKeyFile, hostname}
|
||||
return mclib.RunMain()
|
||||
|
||||
}
|
||||
|
||||
func (c *serverCommand) verifyCert(rootPEM, certPEM []byte, name string) error {
|
||||
roots := x509.NewCertPool()
|
||||
ok := roots.AppendCertsFromPEM(rootPEM)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to parse root certificate")
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
return fmt.Errorf("failed to parse certificate PEM")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse certificate: %v", err.Error())
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
DNSName: name,
|
||||
Roots: roots,
|
||||
}
|
||||
|
||||
if _, err := cert.Verify(opts); err != nil {
|
||||
return fmt.Errorf("failed to verify certificate: %v", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *serverCommand) createServerPorts(cd *simplecobra.Commandeer) error {
|
||||
flags := cd.CobraCommand.Flags()
|
||||
var cerr error
|
||||
@@ -665,36 +774,40 @@ func (c *serverCommand) createServerPorts(cd *simplecobra.Commandeer) error {
|
||||
|
||||
// fixURL massages the baseURL into a form needed for serving
|
||||
// all pages correctly.
|
||||
func (c *serverCommand) fixURL(baseURL, s string, port int) (string, error) {
|
||||
func (c *serverCommand) fixURL(baseURLFromConfig, baseURLFromFlag string, port int) (string, error) {
|
||||
certsSet := (c.tlsCertFile != "" && c.tlsKeyFile != "") || c.tlsAuto
|
||||
useLocalhost := false
|
||||
if s == "" {
|
||||
s = baseURL
|
||||
baseURL := baseURLFromFlag
|
||||
if baseURL == "" {
|
||||
baseURL = baseURLFromConfig
|
||||
useLocalhost = true
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(s, "/") {
|
||||
s = s + "/"
|
||||
if !strings.HasSuffix(baseURL, "/") {
|
||||
baseURL = baseURL + "/"
|
||||
}
|
||||
|
||||
// do an initial parse of the input string
|
||||
u, err := url.Parse(s)
|
||||
u, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// if no Host is defined, then assume that no schema or double-slash were
|
||||
// present in the url. Add a double-slash and make a best effort attempt.
|
||||
if u.Host == "" && s != "/" {
|
||||
s = "//" + s
|
||||
if u.Host == "" && baseURL != "/" {
|
||||
baseURL = "//" + baseURL
|
||||
|
||||
u, err = url.Parse(s)
|
||||
u, err = url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if useLocalhost {
|
||||
if u.Scheme == "https" {
|
||||
if certsSet {
|
||||
u.Scheme = "https"
|
||||
} else if u.Scheme == "https" {
|
||||
u.Scheme = "http"
|
||||
}
|
||||
u.Host = "localhost"
|
||||
@@ -745,6 +858,9 @@ func (c *serverCommand) serve() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// We need the server to share the same logger as the Hugo build (for error counts etc.)
|
||||
c.r.logger = h.Log
|
||||
|
||||
if isMultiHost {
|
||||
for _, l := range conf.configs.ConfigLangs() {
|
||||
baseURLs = append(baseURLs, l.BaseURL().String())
|
||||
@@ -811,10 +927,22 @@ func (c *serverCommand) serve() error {
|
||||
|
||||
for i := range baseURLs {
|
||||
mu, listener, serverURL, endpoint, err := srv.createEndpoint(i)
|
||||
srv := &http.Server{
|
||||
Addr: endpoint,
|
||||
Handler: mu,
|
||||
var srv *http.Server
|
||||
if c.tlsCertFile != "" && c.tlsKeyFile != "" {
|
||||
srv = &http.Server{
|
||||
Addr: endpoint,
|
||||
Handler: mu,
|
||||
TLSConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
srv = &http.Server{
|
||||
Addr: endpoint,
|
||||
Handler: mu,
|
||||
}
|
||||
}
|
||||
|
||||
servers = append(servers, srv)
|
||||
|
||||
if doLiveReload {
|
||||
@@ -828,7 +956,11 @@ func (c *serverCommand) serve() error {
|
||||
}
|
||||
c.r.Printf("Web Server is available at %s (bind address %s)\n", serverURL, c.serverInterface)
|
||||
wg1.Go(func() error {
|
||||
err = srv.Serve(listener)
|
||||
if c.tlsCertFile != "" && c.tlsKeyFile != "" {
|
||||
err = srv.ServeTLS(listener, c.tlsCertFile, c.tlsKeyFile)
|
||||
} else {
|
||||
err = srv.Serve(listener)
|
||||
}
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
@@ -851,7 +983,7 @@ func (c *serverCommand) serve() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(readyFile, b, 0777)
|
||||
err = os.WriteFile(readyFile, b, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -934,8 +1066,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
|
||||
}
|
||||
})
|
||||
|
||||
// prevent spamming the log on changes
|
||||
logger := helpers.NewDistinctErrorLogger()
|
||||
logger := s.c.r.logger
|
||||
|
||||
for _, ev := range staticEvents {
|
||||
// Due to our approach of layering both directories and the content's rendered output
|
||||
@@ -1074,10 +1205,6 @@ func pickOneWriteOrCreatePath(events []fsnotify.Event) string {
|
||||
return name
|
||||
}
|
||||
|
||||
func removeErrorPrefixFromLog(content string) string {
|
||||
return logErrorRe.ReplaceAllLiteralString(content, "")
|
||||
}
|
||||
|
||||
func formatByteCount(b uint64) string {
|
||||
const unit = 1000
|
||||
if b < unit {
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright 2023 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newSimpleTemplateCommand() simplecobra.Commander {
|
||||
return &simpleCommand{
|
||||
name: "template",
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command) {
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func newTemplateCommand() *templateCommand {
|
||||
return &templateCommand{
|
||||
commands: []simplecobra.Commander{},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type templateCommand struct {
|
||||
r *rootCommand
|
||||
|
||||
commands []simplecobra.Commander
|
||||
}
|
||||
|
||||
func (c *templateCommand) Commands() []simplecobra.Commander {
|
||||
return c.commands
|
||||
}
|
||||
|
||||
func (c *templateCommand) Name() string {
|
||||
return "template"
|
||||
}
|
||||
|
||||
func (c *templateCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
|
||||
conf, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), flagsToCfg(cd, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("templateCommand.Run", conf)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *templateCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
cmd := cd.CobraCommand
|
||||
cmd.Short = "Print the site configuration"
|
||||
cmd.Long = `Print the site configuration, both default and custom settings.`
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *templateCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
c.r = cd.Root.Command.(*rootCommand)
|
||||
return nil
|
||||
}
|
||||
@@ -22,29 +22,64 @@ import (
|
||||
// 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 any, from ...any) (any, error) {
|
||||
if len(from) == 0 {
|
||||
return to, nil
|
||||
}
|
||||
tov, toIsNil := indirect(reflect.ValueOf(to))
|
||||
|
||||
toIsNil = toIsNil || to == nil
|
||||
var tot reflect.Type
|
||||
|
||||
if !toIsNil {
|
||||
if tov.Kind() == reflect.Slice {
|
||||
// Create a copy of tov, so we don't modify the original.
|
||||
c := reflect.MakeSlice(tov.Type(), tov.Len(), tov.Len()+len(from))
|
||||
reflect.Copy(c, tov)
|
||||
tov = c
|
||||
}
|
||||
|
||||
if tov.Kind() != reflect.Slice {
|
||||
return nil, fmt.Errorf("expected a slice, got %T", to)
|
||||
}
|
||||
|
||||
tot = tov.Type().Elem()
|
||||
if tot.Kind() == reflect.Slice {
|
||||
totvt := tot.Elem()
|
||||
fromvs := make([]reflect.Value, len(from))
|
||||
for i, f := range from {
|
||||
fromv := reflect.ValueOf(f)
|
||||
fromt := fromv.Type()
|
||||
if fromt.Kind() == reflect.Slice {
|
||||
fromt = fromt.Elem()
|
||||
}
|
||||
if totvt != fromt {
|
||||
return nil, fmt.Errorf("cannot append slice of %s to slice of %s", fromt, totvt)
|
||||
} else {
|
||||
fromvs[i] = fromv
|
||||
}
|
||||
}
|
||||
return reflect.Append(tov, fromvs...).Interface(), nil
|
||||
|
||||
}
|
||||
|
||||
toIsNil = tov.Len() == 0
|
||||
|
||||
if len(from) == 1 {
|
||||
fromv := reflect.ValueOf(from[0])
|
||||
if !fromv.IsValid() {
|
||||
// from[0] is nil
|
||||
return appendToInterfaceSliceFromValues(tov, fromv)
|
||||
}
|
||||
fromt := fromv.Type()
|
||||
if fromt.Kind() == reflect.Slice {
|
||||
fromt = fromt.Elem()
|
||||
}
|
||||
if fromv.Kind() == reflect.Slice {
|
||||
if toIsNil {
|
||||
// If we get nil []string, we just return the []string
|
||||
return from[0], nil
|
||||
}
|
||||
|
||||
fromt := reflect.TypeOf(from[0]).Elem()
|
||||
|
||||
// If we get []string []string, we append the from slice to to
|
||||
if tot == fromt {
|
||||
return reflect.AppendSlice(tov, fromv).Interface(), nil
|
||||
@@ -52,6 +87,7 @@ func Append(to any, from ...any) (any, error) {
|
||||
// Fall back to a []interface{} slice.
|
||||
return appendToInterfaceSliceFromValues(tov, fromv)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +98,7 @@ func Append(to any, from ...any) (any, error) {
|
||||
|
||||
for _, f := range from {
|
||||
fv := reflect.ValueOf(f)
|
||||
if !fv.Type().AssignableTo(tot) {
|
||||
if !fv.IsValid() || !fv.Type().AssignableTo(tot) {
|
||||
// Fall back to a []interface{} slice.
|
||||
tov, _ := indirect(reflect.ValueOf(to))
|
||||
return appendToInterfaceSlice(tov, from...)
|
||||
@@ -77,6 +113,10 @@ func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, erro
|
||||
var tos []any
|
||||
|
||||
for _, slice := range []reflect.Value{slice1, slice2} {
|
||||
if !slice.IsValid() {
|
||||
tos = append(tos, nil)
|
||||
continue
|
||||
}
|
||||
for i := 0; i < slice.Len(); i++ {
|
||||
tos = append(tos, slice.Index(i).Interface())
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestAppend(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
for _, test := range []struct {
|
||||
for i, test := range []struct {
|
||||
start any
|
||||
addend []any
|
||||
expected any
|
||||
@@ -74,6 +74,9 @@ func TestAppend(t *testing.T) {
|
||||
[]any{"c"},
|
||||
false,
|
||||
},
|
||||
{[]string{"a", "b"}, []any{nil}, []any{"a", "b", nil}},
|
||||
{[]string{"a", "b"}, []any{nil, "d", nil}, []any{"a", "b", nil, "d", nil}},
|
||||
{[]any{"a", nil, "c"}, []any{"d", nil, "f"}, []any{"a", nil, "c", "d", nil, "f"}},
|
||||
} {
|
||||
|
||||
result, err := Append(test.start, test.addend...)
|
||||
@@ -85,6 +88,59 @@ func TestAppend(t *testing.T) {
|
||||
}
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(result, qt.DeepEquals, test.expected)
|
||||
c.Assert(result, qt.DeepEquals, test.expected, qt.Commentf("test: [%d] %v", i, test))
|
||||
}
|
||||
}
|
||||
|
||||
// #11093
|
||||
func TestAppendToMultiDimensionalSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
for _, test := range []struct {
|
||||
to any
|
||||
from []any
|
||||
expected any
|
||||
}{
|
||||
{[][]string{{"a", "b"}},
|
||||
[]any{[]string{"c", "d"}},
|
||||
[][]string{
|
||||
{"a", "b"},
|
||||
{"c", "d"},
|
||||
},
|
||||
},
|
||||
{[][]string{{"a", "b"}},
|
||||
[]any{[]string{"c", "d"}, []string{"e", "f"}},
|
||||
[][]string{
|
||||
{"a", "b"},
|
||||
{"c", "d"},
|
||||
{"e", "f"},
|
||||
},
|
||||
},
|
||||
{[][]string{{"a", "b"}},
|
||||
[]any{[]int{1, 2}},
|
||||
false,
|
||||
},
|
||||
} {
|
||||
result, err := Append(test.to, test.from...)
|
||||
if b, ok := test.expected.(bool); ok && !b {
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
} else {
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(result, qt.DeepEquals, test.expected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAppendShouldMakeACopyOfTheInputSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
slice := make([]string, 0, 100)
|
||||
slice = append(slice, "a", "b")
|
||||
result, err := Append(slice, "c")
|
||||
c.Assert(err, qt.IsNil)
|
||||
slice[0] = "d"
|
||||
c.Assert(result, qt.DeepEquals, []string{"a", "b", "c"})
|
||||
c.Assert(slice, qt.DeepEquals, []string{"d", "b"})
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ type LineMatcher struct {
|
||||
}
|
||||
|
||||
// LineMatcherFn is used to match a line with an error.
|
||||
// It returns the column number or 0 if the line was found, but column could not be determinde. Returns -1 if no line match.
|
||||
// It returns the column number or 0 if the line was found, but column could not be determined. Returns -1 if no line match.
|
||||
type LineMatcherFn func(m LineMatcher) int
|
||||
|
||||
// SimpleLineMatcher simply matches by line number.
|
||||
|
||||
@@ -59,11 +59,34 @@ func GetGID() uint64 {
|
||||
return n
|
||||
}
|
||||
|
||||
// IsFeatureNotAvailableError returns true if the given error is or contains a FeatureNotAvailableError.
|
||||
func IsFeatureNotAvailableError(err error) bool {
|
||||
return errors.Is(err, &FeatureNotAvailableError{})
|
||||
}
|
||||
|
||||
// ErrFeatureNotAvailable denotes that a feature is unavailable.
|
||||
//
|
||||
// We will, at least to begin with, make some Hugo features (SCSS with libsass) optional,
|
||||
// and this error is used to signal those situations.
|
||||
var ErrFeatureNotAvailable = errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")
|
||||
var ErrFeatureNotAvailable = &FeatureNotAvailableError{Cause: errors.New("this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information")}
|
||||
|
||||
// FeatureNotAvailableError is an error type used to signal that a feature is not available.
|
||||
type FeatureNotAvailableError struct {
|
||||
Cause error
|
||||
}
|
||||
|
||||
func (e *FeatureNotAvailableError) Unwrap() error {
|
||||
return e.Cause
|
||||
}
|
||||
|
||||
func (e *FeatureNotAvailableError) Error() string {
|
||||
return e.Cause.Error()
|
||||
}
|
||||
|
||||
func (e *FeatureNotAvailableError) Is(target error) bool {
|
||||
_, ok := target.(*FeatureNotAvailableError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Must panics if err != nil.
|
||||
func Must(err error) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package herrors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
@@ -34,3 +35,12 @@ func TestIsNotExist(t *testing.T) {
|
||||
// os.IsNotExist returns false for wrapped errors.
|
||||
c.Assert(IsNotExist(fmt.Errorf("foo: %w", afero.ErrFileNotFound)), qt.Equals, true)
|
||||
}
|
||||
|
||||
func TestIsFeatureNotAvailableError(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(IsFeatureNotAvailableError(ErrFeatureNotAvailable), qt.Equals, true)
|
||||
c.Assert(IsFeatureNotAvailableError(&FeatureNotAvailableError{}), qt.Equals, true)
|
||||
c.Assert(IsFeatureNotAvailableError(errors.New("asdf")), qt.Equals, false)
|
||||
|
||||
}
|
||||
|
||||
@@ -15,11 +15,14 @@ package herrors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
godartsassv1 "github.com/bep/godartsass"
|
||||
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bep/godartsass"
|
||||
"github.com/bep/godartsass/v2"
|
||||
"github.com/bep/golibsass/libsass/libsasserrors"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/common/text"
|
||||
@@ -145,6 +148,8 @@ func (e *fileError) causeString() string {
|
||||
// Avoid repeating the file info in the error message.
|
||||
case godartsass.SassError:
|
||||
return v.Message
|
||||
case godartsassv1.SassError:
|
||||
return v.Message
|
||||
case libsasserrors.Error:
|
||||
return v.Message
|
||||
default:
|
||||
@@ -292,7 +297,7 @@ func extractFileTypePos(err error) (string, text.Position) {
|
||||
}
|
||||
|
||||
// The error type from the minifier contains line number and column number.
|
||||
if line, col := exctractLineNumberAndColumnNumber(err); line >= 0 {
|
||||
if line, col := extractLineNumberAndColumnNumber(err); line >= 0 {
|
||||
pos.LineNumber = line
|
||||
pos.ColumnNumber = col
|
||||
return fileType, pos
|
||||
@@ -364,7 +369,7 @@ func extractOffsetAndType(e error) (int, string) {
|
||||
}
|
||||
}
|
||||
|
||||
func exctractLineNumberAndColumnNumber(e error) (int, int) {
|
||||
func extractLineNumberAndColumnNumber(e error) (int, int) {
|
||||
switch v := e.(type) {
|
||||
case *parse.Error:
|
||||
return v.Line, v.Column
|
||||
@@ -385,6 +390,13 @@ func extractPosition(e error) (pos text.Position) {
|
||||
pos.Filename = filename
|
||||
pos.Offset = start.Offset
|
||||
pos.ColumnNumber = start.Column
|
||||
case godartsassv1.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
|
||||
|
||||
@@ -118,7 +118,7 @@ func SafeCommand(name string, arg ...string) (*exec.Cmd, error) {
|
||||
return exec.Command(bin, arg...), nil
|
||||
}
|
||||
|
||||
// Exec encorces a security policy for commands run via os/exec.
|
||||
// Exec enforces a security policy for commands run via os/exec.
|
||||
type Exec struct {
|
||||
sc security.Config
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bep/clock"
|
||||
"github.com/bep/clocks"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/gohugoio/locales"
|
||||
@@ -75,7 +75,7 @@ var (
|
||||
"December",
|
||||
}
|
||||
|
||||
Clock = clock.System()
|
||||
Clock = clocks.System()
|
||||
)
|
||||
|
||||
func NewTimeFormatter(ltr locales.Translator) TimeFormatter {
|
||||
|
||||
+49
-7
@@ -22,9 +22,13 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
godartsassv1 "github.com/bep/godartsass"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/bep/godartsass"
|
||||
"github.com/bep/godartsass/v2"
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
|
||||
@@ -238,7 +242,10 @@ func GetDependencyListNonGo() []string {
|
||||
}
|
||||
|
||||
if dartSass := dartSassVersion(); dartSass.ProtocolVersion != "" {
|
||||
const dartSassPath = "github.com/sass/dart-sass-embedded"
|
||||
var dartSassPath = "github.com/sass/dart-sass-embedded"
|
||||
if IsDartSassV2() {
|
||||
dartSassPath = "github.com/sass/dart-sass"
|
||||
}
|
||||
deps = append(deps,
|
||||
formatDep(dartSassPath+"/protocol", dartSass.ProtocolVersion),
|
||||
formatDep(dartSassPath+"/compiler", dartSass.CompilerVersion),
|
||||
@@ -283,11 +290,46 @@ type Dependency struct {
|
||||
}
|
||||
|
||||
func dartSassVersion() godartsass.DartSassVersion {
|
||||
// This is also duplicated in the dartsass package.
|
||||
const dartSassEmbeddedBinaryName = "dart-sass-embedded"
|
||||
if !hexec.InPath(dartSassEmbeddedBinaryName) {
|
||||
if DartSassBinaryName == "" {
|
||||
return godartsass.DartSassVersion{}
|
||||
}
|
||||
v, _ := godartsass.Version(dartSassEmbeddedBinaryName)
|
||||
return v
|
||||
if IsDartSassV2() {
|
||||
v, _ := godartsass.Version(DartSassBinaryName)
|
||||
return v
|
||||
}
|
||||
|
||||
v, _ := godartsassv1.Version(DartSassBinaryName)
|
||||
var vv godartsass.DartSassVersion
|
||||
mapstructure.WeakDecode(v, &vv)
|
||||
return vv
|
||||
}
|
||||
|
||||
// DartSassBinaryName is the name of the Dart Sass binary to use.
|
||||
// TODO(beop) find a better place for this.
|
||||
var DartSassBinaryName string
|
||||
|
||||
func init() {
|
||||
DartSassBinaryName = os.Getenv("DART_SASS_BINARY")
|
||||
if DartSassBinaryName == "" {
|
||||
for _, name := range dartSassBinaryNamesV2 {
|
||||
if hexec.InPath(name) {
|
||||
DartSassBinaryName = name
|
||||
break
|
||||
}
|
||||
}
|
||||
if DartSassBinaryName == "" {
|
||||
if hexec.InPath(dartSassBinaryNameV1) {
|
||||
DartSassBinaryName = dartSassBinaryNameV1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
dartSassBinaryNameV1 = "dart-sass-embedded"
|
||||
dartSassBinaryNamesV2 = []string{"dart-sass", "sass"}
|
||||
)
|
||||
|
||||
func IsDartSassV2() bool {
|
||||
return !strings.Contains(DartSassBinaryName, "embedded")
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 112,
|
||||
PatchLevel: 1,
|
||||
Suffix: "",
|
||||
Minor: 116,
|
||||
PatchLevel: 0,
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2023 The Hugo Authors. All rights reserved.
|
||||
// Some functions in this file (see comments) is based on the Go source code,
|
||||
// copyright The Go Authors and governed by a BSD-style license.
|
||||
//
|
||||
// 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 loggers contains some basic logging setup.
|
||||
package loggers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bep/logg"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var bold = color.New(color.Bold)
|
||||
|
||||
// levelColor mapping.
|
||||
var levelColor = [...]*color.Color{
|
||||
logg.LevelDebug: color.New(color.FgWhite),
|
||||
logg.LevelInfo: color.New(color.FgBlue),
|
||||
logg.LevelWarn: color.New(color.FgYellow),
|
||||
logg.LevelError: color.New(color.FgRed),
|
||||
}
|
||||
|
||||
// levelString mapping.
|
||||
var levelString = [...]string{
|
||||
logg.LevelDebug: "DEBUG",
|
||||
logg.LevelInfo: "INFO ",
|
||||
logg.LevelWarn: "WARN ",
|
||||
logg.LevelError: "ERROR",
|
||||
}
|
||||
|
||||
// newDefaultHandler handler.
|
||||
func newDefaultHandler(outWriter, errWriter io.Writer) logg.Handler {
|
||||
return &defaultHandler{
|
||||
outWriter: outWriter,
|
||||
errWriter: errWriter,
|
||||
Padding: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Default Handler implementation.
|
||||
// Based on https://github.com/apex/log/blob/master/handlers/cli/cli.go
|
||||
type defaultHandler struct {
|
||||
mu sync.Mutex
|
||||
outWriter io.Writer // Defaults to os.Stdout.
|
||||
errWriter io.Writer // Defaults to os.Stderr.
|
||||
|
||||
Padding int
|
||||
}
|
||||
|
||||
// HandleLog implements logg.Handler.
|
||||
func (h *defaultHandler) HandleLog(e *logg.Entry) error {
|
||||
color := levelColor[e.Level]
|
||||
level := levelString[e.Level]
|
||||
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
var w io.Writer
|
||||
if e.Level > logg.LevelInfo {
|
||||
w = h.errWriter
|
||||
} else {
|
||||
w = h.outWriter
|
||||
}
|
||||
|
||||
var prefix string
|
||||
for _, field := range e.Fields {
|
||||
if field.Name == FieldNameCmd {
|
||||
prefix = fmt.Sprint(field.Value)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if prefix != "" {
|
||||
prefix = prefix + ": "
|
||||
}
|
||||
|
||||
color.Fprintf(w, "%s %s%s", fmt.Sprintf("%*s", h.Padding+1, level), color.Sprint(prefix), e.Message)
|
||||
|
||||
for _, field := range e.Fields {
|
||||
if strings.HasPrefix(field.Name, reservedFieldNamePrefix) {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, " %s %v", color.Sprint(field.Name), field.Value)
|
||||
}
|
||||
|
||||
fmt.Fprintln(w)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright 2023 The Hugo Authors. All rights reserved.
|
||||
// Some functions in this file (see comments) is based on the Go source code,
|
||||
// copyright The Go Authors and governed by a BSD-style license.
|
||||
//
|
||||
// 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 loggers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
)
|
||||
|
||||
// PanicOnWarningHook panics on warnings.
|
||||
var PanicOnWarningHook = func(e *logg.Entry) error {
|
||||
if e.Level != logg.LevelWarn {
|
||||
return nil
|
||||
}
|
||||
panic(e.Message)
|
||||
}
|
||||
|
||||
func newLogLevelCounter() *logLevelCounter {
|
||||
return &logLevelCounter{
|
||||
counters: make(map[logg.Level]int),
|
||||
}
|
||||
}
|
||||
|
||||
func newLogOnceHandler(threshold logg.Level) *logOnceHandler {
|
||||
return &logOnceHandler{
|
||||
threshold: threshold,
|
||||
seen: make(map[uint64]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func newStopHandler(h ...logg.Handler) *stopHandler {
|
||||
return &stopHandler{
|
||||
handlers: h,
|
||||
}
|
||||
}
|
||||
|
||||
func newSuppressStatementsHandler(statements map[string]bool) *suppressStatementsHandler {
|
||||
return &suppressStatementsHandler{
|
||||
statements: statements,
|
||||
}
|
||||
}
|
||||
|
||||
type logLevelCounter struct {
|
||||
mu sync.RWMutex
|
||||
counters map[logg.Level]int
|
||||
}
|
||||
|
||||
func (h *logLevelCounter) HandleLog(e *logg.Entry) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.counters[e.Level]++
|
||||
return nil
|
||||
}
|
||||
|
||||
var stopError = fmt.Errorf("stop")
|
||||
|
||||
type logOnceHandler struct {
|
||||
threshold logg.Level
|
||||
mu sync.Mutex
|
||||
seen map[uint64]bool
|
||||
}
|
||||
|
||||
func (h *logOnceHandler) HandleLog(e *logg.Entry) error {
|
||||
if e.Level < h.threshold {
|
||||
// We typically only want to enable this for warnings and above.
|
||||
// The common use case is that many go routines may log the same error.
|
||||
return nil
|
||||
}
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
hash := identity.HashUint64(e.Level, e.Message, e.Fields)
|
||||
if h.seen[hash] {
|
||||
return stopError
|
||||
}
|
||||
h.seen[hash] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *logOnceHandler) reset() {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.seen = make(map[uint64]bool)
|
||||
}
|
||||
|
||||
type stopHandler struct {
|
||||
handlers []logg.Handler
|
||||
}
|
||||
|
||||
// HandleLog implements logg.Handler.
|
||||
func (h *stopHandler) HandleLog(e *logg.Entry) error {
|
||||
for _, handler := range h.handlers {
|
||||
if err := handler.HandleLog(e); err != nil {
|
||||
if err == stopError {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type suppressStatementsHandler struct {
|
||||
statements map[string]bool
|
||||
}
|
||||
|
||||
func (h *suppressStatementsHandler) HandleLog(e *logg.Entry) error {
|
||||
for _, field := range e.Fields {
|
||||
if field.Name == FieldNameStatementID {
|
||||
if h.statements[field.Value.(string)] {
|
||||
return stopError
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// replacer creates a new log handler that does string replacement in log messages.
|
||||
func replacer(repl *strings.Replacer) logg.Handler {
|
||||
return logg.HandlerFunc(func(e *logg.Entry) error {
|
||||
e.Message = repl.Replace(e.Message)
|
||||
for i, field := range e.Fields {
|
||||
if s, ok := field.Value.(string); ok {
|
||||
e.Fields[i].Value = repl.Replace(s)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// whiteSpaceTrimmer creates a new log handler that trims whitespace from log messages and string fields.
|
||||
func whiteSpaceTrimmer() logg.Handler {
|
||||
return logg.HandlerFunc(func(e *logg.Entry) error {
|
||||
e.Message = strings.TrimSpace(e.Message)
|
||||
for i, field := range e.Fields {
|
||||
if s, ok := field.Value.(string); ok {
|
||||
e.Fields[i].Value = strings.TrimSpace(s)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2023 The Hugo Authors. All rights reserved.
|
||||
// Some functions in this file (see comments) is based on the Go source code,
|
||||
// copyright The Go Authors and governed by a BSD-style license.
|
||||
//
|
||||
// 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 loggers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bep/logg"
|
||||
)
|
||||
|
||||
// newNoColoursHandler creates a new NoColoursHandler
|
||||
func newNoColoursHandler(outWriter, errWriter io.Writer, noLevelPrefix bool, predicate func(*logg.Entry) bool) *noColoursHandler {
|
||||
if predicate == nil {
|
||||
predicate = func(e *logg.Entry) bool { return true }
|
||||
}
|
||||
return &noColoursHandler{
|
||||
noLevelPrefix: noLevelPrefix,
|
||||
outWriter: outWriter,
|
||||
errWriter: errWriter,
|
||||
predicate: predicate,
|
||||
}
|
||||
}
|
||||
|
||||
type noColoursHandler struct {
|
||||
mu sync.Mutex
|
||||
outWriter io.Writer // Defaults to os.Stdout.
|
||||
errWriter io.Writer // Defaults to os.Stderr.
|
||||
predicate func(*logg.Entry) bool
|
||||
noLevelPrefix bool
|
||||
}
|
||||
|
||||
func (h *noColoursHandler) HandleLog(e *logg.Entry) error {
|
||||
if !h.predicate(e) {
|
||||
return nil
|
||||
}
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
var w io.Writer
|
||||
if e.Level > logg.LevelInfo {
|
||||
w = h.errWriter
|
||||
} else {
|
||||
w = h.outWriter
|
||||
}
|
||||
|
||||
var prefix string
|
||||
for _, field := range e.Fields {
|
||||
if field.Name == FieldNameCmd {
|
||||
prefix = fmt.Sprint(field.Value)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if prefix != "" {
|
||||
prefix = prefix + ": "
|
||||
}
|
||||
|
||||
if h.noLevelPrefix {
|
||||
fmt.Fprintf(w, "%s%s", prefix, e.Message)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s %s%s", levelString[e.Level], prefix, e.Message)
|
||||
}
|
||||
|
||||
for _, field := range e.Fields {
|
||||
if strings.HasPrefix(field.Name, reservedFieldNamePrefix) {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, " %s %q", field.Name, field.Value)
|
||||
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,63 +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 loggers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// IgnorableLogger is a logger that ignores certain log statements.
|
||||
type IgnorableLogger interface {
|
||||
Logger
|
||||
Errorsf(statementID, format string, v ...any)
|
||||
Apply(logger Logger) IgnorableLogger
|
||||
}
|
||||
|
||||
type ignorableLogger struct {
|
||||
Logger
|
||||
statements map[string]bool
|
||||
}
|
||||
|
||||
// NewIgnorableLogger wraps the given logger and ignores the log statement IDs given.
|
||||
func NewIgnorableLogger(logger Logger, statements map[string]bool) IgnorableLogger {
|
||||
if statements == nil {
|
||||
statements = make(map[string]bool)
|
||||
}
|
||||
return ignorableLogger{
|
||||
Logger: logger,
|
||||
statements: statements,
|
||||
}
|
||||
}
|
||||
|
||||
// Errorsf logs statementID as an ERROR if not configured as ignoreable.
|
||||
func (l ignorableLogger) Errorsf(statementID, format string, v ...any) {
|
||||
if l.statements[statementID] {
|
||||
// Ignore.
|
||||
return
|
||||
}
|
||||
ignoreMsg := fmt.Sprintf(`
|
||||
If you feel that this should not be logged as an ERROR, you can ignore it by adding this to your site config:
|
||||
ignoreErrors = [%q]`, statementID)
|
||||
|
||||
format += ignoreMsg
|
||||
|
||||
l.Errorf(format, v...)
|
||||
}
|
||||
|
||||
func (l ignorableLogger) Apply(logger Logger) IgnorableLogger {
|
||||
return ignorableLogger{
|
||||
Logger: logger,
|
||||
statements: l.statements,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
// Copyright 2023 The Hugo Authors. All rights reserved.
|
||||
// Some functions in this file (see comments) is based on the Go source code,
|
||||
// copyright The Go Authors and governed by a BSD-style license.
|
||||
//
|
||||
// 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 loggers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/bep/logg/handlers/multi"
|
||||
"github.com/gohugoio/hugo/common/terminal"
|
||||
)
|
||||
|
||||
var (
|
||||
reservedFieldNamePrefix = "__h_field_"
|
||||
// FieldNameCmd is the name of the field that holds the command name.
|
||||
FieldNameCmd = reservedFieldNamePrefix + "_cmd"
|
||||
// Used to suppress statements.
|
||||
FieldNameStatementID = reservedFieldNamePrefix + "__h_field_statement_id"
|
||||
)
|
||||
|
||||
// Options defines options for the logger.
|
||||
type Options struct {
|
||||
Level logg.Level
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Distinct bool
|
||||
StoreErrors bool
|
||||
HandlerPost func(e *logg.Entry) error
|
||||
SuppresssStatements map[string]bool
|
||||
}
|
||||
|
||||
// New creates a new logger with the given options.
|
||||
func New(opts Options) Logger {
|
||||
if opts.Stdout == nil {
|
||||
opts.Stdout = os.Stdout
|
||||
}
|
||||
if opts.Stderr == nil {
|
||||
opts.Stderr = os.Stdout
|
||||
}
|
||||
if opts.Level == 0 {
|
||||
opts.Level = logg.LevelWarn
|
||||
}
|
||||
|
||||
var logHandler logg.Handler
|
||||
if terminal.PrintANSIColors(os.Stdout) {
|
||||
logHandler = newDefaultHandler(opts.Stdout, opts.Stderr)
|
||||
} else {
|
||||
logHandler = newNoColoursHandler(opts.Stdout, opts.Stderr, false, nil)
|
||||
}
|
||||
|
||||
errorsw := &strings.Builder{}
|
||||
logCounters := newLogLevelCounter()
|
||||
handlers := []logg.Handler{
|
||||
whiteSpaceTrimmer(),
|
||||
logHandler,
|
||||
logCounters,
|
||||
}
|
||||
|
||||
if opts.HandlerPost != nil {
|
||||
var hookHandler logg.HandlerFunc = func(e *logg.Entry) error {
|
||||
opts.HandlerPost(e)
|
||||
return nil
|
||||
}
|
||||
handlers = append(handlers, hookHandler)
|
||||
}
|
||||
|
||||
if opts.StoreErrors {
|
||||
h := newNoColoursHandler(io.Discard, errorsw, true, func(e *logg.Entry) bool {
|
||||
return e.Level >= logg.LevelError
|
||||
})
|
||||
|
||||
handlers = append(handlers, h)
|
||||
}
|
||||
|
||||
logHandler = multi.New(handlers...)
|
||||
|
||||
var logOnce *logOnceHandler
|
||||
if opts.Distinct {
|
||||
logOnce = newLogOnceHandler(logg.LevelWarn)
|
||||
logHandler = newStopHandler(logOnce, logHandler)
|
||||
}
|
||||
|
||||
if opts.SuppresssStatements != nil && len(opts.SuppresssStatements) > 0 {
|
||||
logHandler = newStopHandler(newSuppressStatementsHandler(opts.SuppresssStatements), logHandler)
|
||||
}
|
||||
|
||||
logger := logg.New(
|
||||
logg.Options{
|
||||
Level: opts.Level,
|
||||
Handler: logHandler,
|
||||
},
|
||||
)
|
||||
|
||||
l := logger.WithLevel(opts.Level)
|
||||
|
||||
reset := func() {
|
||||
logCounters.mu.Lock()
|
||||
defer logCounters.mu.Unlock()
|
||||
logCounters.counters = make(map[logg.Level]int)
|
||||
errorsw.Reset()
|
||||
if logOnce != nil {
|
||||
logOnce.reset()
|
||||
}
|
||||
}
|
||||
|
||||
return &logAdapter{
|
||||
logCounters: logCounters,
|
||||
errors: errorsw,
|
||||
reset: reset,
|
||||
out: opts.Stdout,
|
||||
level: opts.Level,
|
||||
logger: logger,
|
||||
debugl: l.WithLevel(logg.LevelDebug),
|
||||
infol: l.WithLevel(logg.LevelInfo),
|
||||
warnl: l.WithLevel(logg.LevelWarn),
|
||||
errorl: l.WithLevel(logg.LevelError),
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefault creates a new logger with the default options.
|
||||
func NewDefault() Logger {
|
||||
opts := Options{
|
||||
Distinct: true,
|
||||
Level: logg.LevelWarn,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stdout,
|
||||
}
|
||||
return New(opts)
|
||||
}
|
||||
|
||||
func LevelLoggerToWriter(l logg.LevelLogger) io.Writer {
|
||||
return logWriter{l: l}
|
||||
}
|
||||
|
||||
type Logger interface {
|
||||
Debugf(format string, v ...any)
|
||||
Debugln(v ...any)
|
||||
Error() logg.LevelLogger
|
||||
Errorf(format string, v ...any)
|
||||
Errorln(v ...any)
|
||||
Errors() string
|
||||
Errorsf(id, format string, v ...any)
|
||||
Info() logg.LevelLogger
|
||||
InfoCommand(command string) logg.LevelLogger
|
||||
Infof(format string, v ...any)
|
||||
Infoln(v ...any)
|
||||
Level() logg.Level
|
||||
LoggCount(logg.Level) int
|
||||
Logger() logg.Logger
|
||||
Out() io.Writer
|
||||
Printf(format string, v ...any)
|
||||
Println(v ...any)
|
||||
PrintTimerIfDelayed(start time.Time, name string)
|
||||
Reset()
|
||||
Warn() logg.LevelLogger
|
||||
WarnCommand(command string) logg.LevelLogger
|
||||
Warnf(format string, v ...any)
|
||||
Warnln(v ...any)
|
||||
}
|
||||
|
||||
type logAdapter struct {
|
||||
logCounters *logLevelCounter
|
||||
errors *strings.Builder
|
||||
reset func()
|
||||
out io.Writer
|
||||
level logg.Level
|
||||
logger logg.Logger
|
||||
debugl logg.LevelLogger
|
||||
infol logg.LevelLogger
|
||||
warnl logg.LevelLogger
|
||||
errorl logg.LevelLogger
|
||||
}
|
||||
|
||||
func (l *logAdapter) Debugf(format string, v ...any) {
|
||||
l.debugl.Logf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Debugln(v ...any) {
|
||||
l.debugl.Logf(l.sprint(v...))
|
||||
}
|
||||
|
||||
func (l *logAdapter) Info() logg.LevelLogger {
|
||||
return l.infol
|
||||
}
|
||||
|
||||
func (l *logAdapter) InfoCommand(command string) logg.LevelLogger {
|
||||
return l.infol.WithField(FieldNameCmd, command)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Infof(format string, v ...any) {
|
||||
l.infol.Logf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Infoln(v ...any) {
|
||||
l.infol.Logf(l.sprint(v...))
|
||||
}
|
||||
|
||||
func (l *logAdapter) Level() logg.Level {
|
||||
return l.level
|
||||
}
|
||||
|
||||
func (l *logAdapter) LoggCount(level logg.Level) int {
|
||||
l.logCounters.mu.RLock()
|
||||
defer l.logCounters.mu.RUnlock()
|
||||
return l.logCounters.counters[level]
|
||||
}
|
||||
|
||||
func (l *logAdapter) Logger() logg.Logger {
|
||||
return l.logger
|
||||
}
|
||||
|
||||
func (l *logAdapter) Out() io.Writer {
|
||||
return l.out
|
||||
}
|
||||
|
||||
// PrintTimerIfDelayed prints a time statement to the FEEDBACK logger
|
||||
// if considerable time is spent.
|
||||
func (l *logAdapter) PrintTimerIfDelayed(start time.Time, name string) {
|
||||
elapsed := time.Since(start)
|
||||
milli := int(1000 * elapsed.Seconds())
|
||||
if milli < 500 {
|
||||
return
|
||||
}
|
||||
l.Printf("%s in %v ms", name, milli)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Printf(format string, v ...any) {
|
||||
// Add trailing newline if not present.
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
fmt.Fprintf(l.out, format, v...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Println(v ...any) {
|
||||
fmt.Fprintln(l.out, v...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Reset() {
|
||||
l.reset()
|
||||
}
|
||||
|
||||
func (l *logAdapter) Warn() logg.LevelLogger {
|
||||
return l.warnl
|
||||
}
|
||||
|
||||
func (l *logAdapter) Warnf(format string, v ...any) {
|
||||
l.warnl.Logf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) WarnCommand(command string) logg.LevelLogger {
|
||||
return l.warnl.WithField(FieldNameCmd, command)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Warnln(v ...any) {
|
||||
l.warnl.Logf(l.sprint(v...))
|
||||
}
|
||||
|
||||
func (l *logAdapter) Error() logg.LevelLogger {
|
||||
return l.errorl
|
||||
}
|
||||
|
||||
func (l *logAdapter) Errorf(format string, v ...any) {
|
||||
l.errorl.Logf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Errorln(v ...any) {
|
||||
l.errorl.Logf(l.sprint(v...))
|
||||
}
|
||||
|
||||
func (l *logAdapter) Errors() string {
|
||||
return l.errors.String()
|
||||
}
|
||||
|
||||
func (l *logAdapter) Errorsf(id, format string, v ...any) {
|
||||
l.errorl.WithField(FieldNameStatementID, id).Logf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) sprint(v ...any) string {
|
||||
return strings.TrimRight(fmt.Sprintln(v...), "\n")
|
||||
}
|
||||
|
||||
type logWriter struct {
|
||||
l logg.LevelLogger
|
||||
}
|
||||
|
||||
func (w logWriter) Write(p []byte) (n int, err error) {
|
||||
w.l.Log(logg.String(string(p)))
|
||||
return len(p), nil
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
// Copyright 2023 The Hugo Authors. All rights reserved.
|
||||
// Some functions in this file (see comments) is based on the Go source code,
|
||||
// copyright The Go Authors and governed by a BSD-style license.
|
||||
//
|
||||
// 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 loggers_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/bep/logg"
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
)
|
||||
|
||||
func TestLogDistinct(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
opts := loggers.Options{
|
||||
Distinct: true,
|
||||
StoreErrors: true,
|
||||
Stdout: io.Discard,
|
||||
Stderr: io.Discard,
|
||||
}
|
||||
|
||||
l := loggers.New(opts)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
l.Errorln("error 1")
|
||||
l.Errorln("error 2")
|
||||
l.Warnln("warn 1")
|
||||
}
|
||||
c.Assert(strings.Count(l.Errors(), "error 1"), qt.Equals, 1)
|
||||
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 2)
|
||||
c.Assert(l.LoggCount(logg.LevelWarn), qt.Equals, 1)
|
||||
}
|
||||
|
||||
func TestHookLast(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
opts := loggers.Options{
|
||||
HandlerPost: func(e *logg.Entry) error {
|
||||
panic(e.Message)
|
||||
},
|
||||
Stdout: io.Discard,
|
||||
Stderr: io.Discard,
|
||||
}
|
||||
|
||||
l := loggers.New(opts)
|
||||
|
||||
c.Assert(func() { l.Warnln("warn 1") }, qt.PanicMatches, "warn 1")
|
||||
}
|
||||
|
||||
func TestOptionStoreErrors(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
var sb strings.Builder
|
||||
|
||||
opts := loggers.Options{
|
||||
StoreErrors: true,
|
||||
Stderr: &sb,
|
||||
Stdout: &sb,
|
||||
}
|
||||
|
||||
l := loggers.New(opts)
|
||||
l.Errorln("error 1")
|
||||
l.Errorln("error 2")
|
||||
|
||||
errorsStr := l.Errors()
|
||||
|
||||
c.Assert(errorsStr, qt.Contains, "error 1")
|
||||
c.Assert(errorsStr, qt.Not(qt.Contains), "ERROR")
|
||||
|
||||
c.Assert(sb.String(), qt.Contains, "error 1")
|
||||
c.Assert(sb.String(), qt.Contains, "ERROR")
|
||||
|
||||
}
|
||||
|
||||
func TestLogCount(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
opts := loggers.Options{
|
||||
StoreErrors: true,
|
||||
}
|
||||
|
||||
l := loggers.New(opts)
|
||||
l.Errorln("error 1")
|
||||
l.Errorln("error 2")
|
||||
l.Warnln("warn 1")
|
||||
|
||||
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 2)
|
||||
c.Assert(l.LoggCount(logg.LevelWarn), qt.Equals, 1)
|
||||
c.Assert(l.LoggCount(logg.LevelInfo), qt.Equals, 0)
|
||||
}
|
||||
|
||||
func TestSuppressStatements(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
opts := loggers.Options{
|
||||
StoreErrors: true,
|
||||
SuppresssStatements: map[string]bool{
|
||||
"error-1": true,
|
||||
},
|
||||
}
|
||||
|
||||
l := loggers.New(opts)
|
||||
l.Error().WithField(loggers.FieldNameStatementID, "error-1").Logf("error 1")
|
||||
l.Errorln("error 2")
|
||||
|
||||
errorsStr := l.Errors()
|
||||
|
||||
c.Assert(errorsStr, qt.Not(qt.Contains), "error 1")
|
||||
c.Assert(errorsStr, qt.Contains, "error 2")
|
||||
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 1)
|
||||
|
||||
}
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
opts := loggers.Options{
|
||||
StoreErrors: true,
|
||||
Distinct: true,
|
||||
Stdout: io.Discard,
|
||||
Stderr: io.Discard,
|
||||
}
|
||||
|
||||
l := loggers.New(opts)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
l.Errorln("error 1")
|
||||
l.Errorln("error 2")
|
||||
l.Errorln("error 1")
|
||||
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 2)
|
||||
|
||||
l.Reset()
|
||||
|
||||
errorsStr := l.Errors()
|
||||
|
||||
c.Assert(errorsStr, qt.Equals, "")
|
||||
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 0)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2023 The Hugo Authors. All rights reserved.
|
||||
// Some functions in this file (see comments) is based on the Go source code,
|
||||
// copyright The Go Authors and governed by a BSD-style license.
|
||||
//
|
||||
// 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 loggers
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/bep/logg"
|
||||
)
|
||||
|
||||
func InitGlobalLogger(panicOnWarnings bool) {
|
||||
logMu.Lock()
|
||||
defer logMu.Unlock()
|
||||
var logHookLast func(e *logg.Entry) error
|
||||
if panicOnWarnings {
|
||||
logHookLast = PanicOnWarningHook
|
||||
}
|
||||
|
||||
log = New(
|
||||
Options{
|
||||
Distinct: true,
|
||||
HandlerPost: logHookLast,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
var logMu sync.Mutex
|
||||
|
||||
func Log() Logger {
|
||||
logMu.Lock()
|
||||
defer logMu.Unlock()
|
||||
return log
|
||||
}
|
||||
|
||||
// The global logger.
|
||||
var log Logger
|
||||
|
||||
func init() {
|
||||
InitGlobalLogger(false)
|
||||
}
|
||||
@@ -1,355 +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 loggers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/common/terminal"
|
||||
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var (
|
||||
// Counts ERROR logs to the global jww logger.
|
||||
GlobalErrorCounter *jww.Counter
|
||||
PanicOnWarning atomic.Bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
GlobalErrorCounter = &jww.Counter{}
|
||||
jww.SetLogListeners(jww.LogCounter(GlobalErrorCounter, jww.LevelError))
|
||||
}
|
||||
|
||||
func LoggerToWriterWithPrefix(logger *log.Logger, prefix string) io.Writer {
|
||||
return prefixWriter{
|
||||
logger: logger,
|
||||
prefix: prefix,
|
||||
}
|
||||
}
|
||||
|
||||
type prefixWriter struct {
|
||||
logger *log.Logger
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (w prefixWriter) Write(p []byte) (n int, err error) {
|
||||
w.logger.Printf("%s: %s", w.prefix, p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type Logger interface {
|
||||
Printf(format string, v ...any)
|
||||
Println(v ...any)
|
||||
PrintTimerIfDelayed(start time.Time, name string)
|
||||
Debug() *log.Logger
|
||||
Debugf(format string, v ...any)
|
||||
Debugln(v ...any)
|
||||
Info() *log.Logger
|
||||
Infof(format string, v ...any)
|
||||
Infoln(v ...any)
|
||||
Warn() *log.Logger
|
||||
Warnf(format string, v ...any)
|
||||
Warnln(v ...any)
|
||||
Error() *log.Logger
|
||||
Errorf(format string, v ...any)
|
||||
Errorln(v ...any)
|
||||
Errors() string
|
||||
|
||||
Out() io.Writer
|
||||
|
||||
Reset()
|
||||
|
||||
// Used in tests.
|
||||
LogCounters() *LogCounters
|
||||
}
|
||||
|
||||
type LogCounters struct {
|
||||
ErrorCounter *jww.Counter
|
||||
WarnCounter *jww.Counter
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
*jww.Notepad
|
||||
|
||||
// The writer that represents stdout.
|
||||
// Will be io.Discard when in quiet mode.
|
||||
out io.Writer
|
||||
|
||||
logCounters *LogCounters
|
||||
|
||||
// This is only set in server mode.
|
||||
errors *bytes.Buffer
|
||||
}
|
||||
|
||||
func (l *logger) Printf(format string, v ...any) {
|
||||
l.FEEDBACK.Printf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Println(v ...any) {
|
||||
l.FEEDBACK.Println(v...)
|
||||
}
|
||||
|
||||
func (l *logger) Debug() *log.Logger {
|
||||
return l.DEBUG
|
||||
}
|
||||
|
||||
func (l *logger) Debugf(format string, v ...any) {
|
||||
l.DEBUG.Printf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Debugln(v ...any) {
|
||||
l.DEBUG.Println(v...)
|
||||
}
|
||||
|
||||
func (l *logger) Infof(format string, v ...any) {
|
||||
l.INFO.Printf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Infoln(v ...any) {
|
||||
l.INFO.Println(v...)
|
||||
}
|
||||
|
||||
func (l *logger) Info() *log.Logger {
|
||||
return l.INFO
|
||||
}
|
||||
|
||||
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.Load() {
|
||||
panic(panicOnWarningMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logger) Warnln(v ...any) {
|
||||
l.WARN.Println(v...)
|
||||
if PanicOnWarning.Load() {
|
||||
panic(panicOnWarningMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logger) Warn() *log.Logger {
|
||||
return l.WARN
|
||||
}
|
||||
|
||||
func (l *logger) Errorf(format string, v ...any) {
|
||||
l.ERROR.Printf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Errorln(v ...any) {
|
||||
l.ERROR.Println(v...)
|
||||
}
|
||||
|
||||
func (l *logger) Error() *log.Logger {
|
||||
return l.ERROR
|
||||
}
|
||||
|
||||
func (l *logger) LogCounters() *LogCounters {
|
||||
return l.logCounters
|
||||
}
|
||||
|
||||
func (l *logger) Out() io.Writer {
|
||||
return l.out
|
||||
}
|
||||
|
||||
// PrintTimerIfDelayed prints a time statement to the FEEDBACK logger
|
||||
// if considerable time is spent.
|
||||
func (l *logger) PrintTimerIfDelayed(start time.Time, name string) {
|
||||
elapsed := time.Since(start)
|
||||
milli := int(1000 * elapsed.Seconds())
|
||||
if milli < 500 {
|
||||
return
|
||||
}
|
||||
l.Printf("%s in %v ms", name, milli)
|
||||
}
|
||||
|
||||
func (l *logger) PrintTimer(start time.Time, name string) {
|
||||
elapsed := time.Since(start)
|
||||
milli := int(1000 * elapsed.Seconds())
|
||||
l.Printf("%s in %v ms", name, milli)
|
||||
}
|
||||
|
||||
func (l *logger) Errors() string {
|
||||
if l.errors == nil {
|
||||
return ""
|
||||
}
|
||||
return ansiColorRe.ReplaceAllString(l.errors.String(), "")
|
||||
}
|
||||
|
||||
// Reset resets the logger's internal state.
|
||||
func (l *logger) Reset() {
|
||||
l.logCounters.ErrorCounter.Reset()
|
||||
if l.errors != nil {
|
||||
l.errors.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
// NewLogger creates a new Logger for the given thresholds
|
||||
func NewLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer, saveErrors bool) Logger {
|
||||
return newLogger(stdoutThreshold, logThreshold, outHandle, logHandle, saveErrors)
|
||||
}
|
||||
|
||||
// NewDebugLogger is a convenience function to create a debug logger.
|
||||
func NewDebugLogger() Logger {
|
||||
return NewBasicLogger(jww.LevelDebug)
|
||||
}
|
||||
|
||||
// NewWarningLogger is a convenience function to create a warning logger.
|
||||
func NewWarningLogger() Logger {
|
||||
return NewBasicLogger(jww.LevelWarn)
|
||||
}
|
||||
|
||||
// NewInfoLogger is a convenience function to create a info logger.
|
||||
func NewInfoLogger() Logger {
|
||||
return NewBasicLogger(jww.LevelInfo)
|
||||
}
|
||||
|
||||
// NewErrorLogger is a convenience function to create an error logger.
|
||||
func NewErrorLogger() Logger {
|
||||
return NewBasicLogger(jww.LevelError)
|
||||
}
|
||||
|
||||
// NewBasicLogger creates a new basic logger writing to Stdout.
|
||||
func NewBasicLogger(t jww.Threshold) Logger {
|
||||
return newLogger(t, jww.LevelError, os.Stdout, io.Discard, false)
|
||||
}
|
||||
|
||||
// NewBasicLoggerForWriter creates a new basic logger writing to w.
|
||||
func NewBasicLoggerForWriter(t jww.Threshold, w io.Writer) Logger {
|
||||
return newLogger(t, jww.LevelError, w, io.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)")
|
||||
)
|
||||
|
||||
type ansiCleaner struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (a ansiCleaner) Write(p []byte) (n int, err error) {
|
||||
return a.w.Write(ansiColorRe.ReplaceAll(p, []byte("")))
|
||||
}
|
||||
|
||||
type labelColorizer struct {
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (a labelColorizer) Write(p []byte) (n int, err error) {
|
||||
replaced := errorRe.ReplaceAllStringFunc(string(p), func(m string) string {
|
||||
switch m {
|
||||
case "ERROR", "FATAL":
|
||||
return terminal.Error(m)
|
||||
case "WARN":
|
||||
return terminal.Warning(m)
|
||||
default:
|
||||
return m
|
||||
}
|
||||
})
|
||||
// io.MultiWriter will abort if we return a bigger write count than input
|
||||
// bytes, so we lie a little.
|
||||
_, err = a.w.Write([]byte(replaced))
|
||||
return len(p), err
|
||||
}
|
||||
|
||||
// InitGlobalLogger initializes the global logger, used in some rare cases.
|
||||
func InitGlobalLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer) {
|
||||
outHandle, logHandle = getLogWriters(outHandle, logHandle)
|
||||
|
||||
jww.SetStdoutOutput(outHandle)
|
||||
jww.SetLogOutput(logHandle)
|
||||
jww.SetLogThreshold(logThreshold)
|
||||
jww.SetStdoutThreshold(stdoutThreshold)
|
||||
}
|
||||
|
||||
func getLogWriters(outHandle, logHandle io.Writer) (io.Writer, io.Writer) {
|
||||
isTerm := terminal.PrintANSIColors(os.Stdout)
|
||||
if logHandle != io.Discard && isTerm {
|
||||
// Remove any Ansi coloring from log output
|
||||
logHandle = ansiCleaner{w: logHandle}
|
||||
}
|
||||
|
||||
if isTerm {
|
||||
outHandle = labelColorizer{w: outHandle}
|
||||
}
|
||||
|
||||
return outHandle, logHandle
|
||||
}
|
||||
|
||||
type fatalLogWriter int
|
||||
|
||||
func (s fatalLogWriter) Write(p []byte) (n int, err error) {
|
||||
trace := make([]byte, 1500)
|
||||
runtime.Stack(trace, true)
|
||||
fmt.Printf("\n===========\n\n%s\n", trace)
|
||||
os.Exit(-1)
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var fatalLogListener = func(t jww.Threshold) io.Writer {
|
||||
if t != jww.LevelError {
|
||||
// Only interested in ERROR
|
||||
return nil
|
||||
}
|
||||
|
||||
return new(fatalLogWriter)
|
||||
}
|
||||
|
||||
func newLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer, saveErrors bool) *logger {
|
||||
errorCounter := &jww.Counter{}
|
||||
warnCounter := &jww.Counter{}
|
||||
outHandle, logHandle = getLogWriters(outHandle, logHandle)
|
||||
|
||||
listeners := []jww.LogListener{jww.LogCounter(errorCounter, jww.LevelError), jww.LogCounter(warnCounter, jww.LevelWarn)}
|
||||
var errorBuff *bytes.Buffer
|
||||
if saveErrors {
|
||||
errorBuff = new(bytes.Buffer)
|
||||
errorCapture := func(t jww.Threshold) io.Writer {
|
||||
if t != jww.LevelError {
|
||||
// Only interested in ERROR
|
||||
return nil
|
||||
}
|
||||
return errorBuff
|
||||
}
|
||||
|
||||
listeners = append(listeners, errorCapture)
|
||||
}
|
||||
|
||||
return &logger{
|
||||
Notepad: jww.NewNotepad(stdoutThreshold, logThreshold, outHandle, logHandle, "", log.Ldate|log.Ltime, listeners...),
|
||||
out: outHandle,
|
||||
logCounters: &LogCounters{
|
||||
ErrorCounter: errorCounter,
|
||||
WarnCounter: warnCounter,
|
||||
},
|
||||
errors: errorBuff,
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright 2018 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 loggers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
l := NewWarningLogger()
|
||||
|
||||
l.Errorln("One error")
|
||||
l.Errorln("Two error")
|
||||
l.Warnln("A warning")
|
||||
|
||||
c.Assert(l.LogCounters().ErrorCounter.Count(), qt.Equals, uint64(2))
|
||||
}
|
||||
|
||||
func TestLoggerToWriterWithPrefix(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
logger := log.New(&b, "", 0)
|
||||
|
||||
w := LoggerToWriterWithPrefix(logger, "myprefix")
|
||||
|
||||
fmt.Fprint(w, "Hello Hugo!")
|
||||
|
||||
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 |")
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func extractFilename(in, ext, base, pathSeparator string) (name string) {
|
||||
// return the filename minus the extension (and the ".")
|
||||
name = base[:strings.LastIndex(base, ".")]
|
||||
} else {
|
||||
// no extension case so just return base, which willi
|
||||
// no extension case so just return base, which will
|
||||
// be the filename
|
||||
name = base
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ type Config struct {
|
||||
Minify minifiers.MinifyConfig `mapstructure:"-"`
|
||||
|
||||
// Permalink configuration.
|
||||
Permalinks map[string]string `mapstructure:"-"`
|
||||
Permalinks map[string]map[string]string `mapstructure:"-"`
|
||||
|
||||
// Taxonomy configuration.
|
||||
Taxonomies map[string]string `mapstructure:"-"`
|
||||
@@ -191,6 +191,22 @@ type configCompiler interface {
|
||||
func (c Config) cloneForLang() *Config {
|
||||
x := c
|
||||
x.C = nil
|
||||
copyStringSlice := func(in []string) []string {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, len(in))
|
||||
copy(out, in)
|
||||
return out
|
||||
}
|
||||
|
||||
// Copy all the slices to avoid sharing.
|
||||
x.DisableKinds = copyStringSlice(x.DisableKinds)
|
||||
x.DisableLanguages = copyStringSlice(x.DisableLanguages)
|
||||
x.MainSections = copyStringSlice(x.MainSections)
|
||||
x.IgnoreErrors = copyStringSlice(x.IgnoreErrors)
|
||||
x.IgnoreFiles = copyStringSlice(x.IgnoreFiles)
|
||||
x.Theme = copyStringSlice(x.Theme)
|
||||
|
||||
// Collapse all static dirs to one.
|
||||
x.StaticDir = x.staticDirs()
|
||||
@@ -258,6 +274,14 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
}
|
||||
disabledLangs[lang] = true
|
||||
}
|
||||
for lang, language := range c.Languages {
|
||||
if language.Disabled {
|
||||
disabledLangs[lang] = true
|
||||
if lang == c.DefaultContentLanguage {
|
||||
return fmt.Errorf("cannot disable default content language %q", lang)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ignoredErrors := make(map[string]bool)
|
||||
for _, err := range c.IgnoreErrors {
|
||||
@@ -408,10 +432,10 @@ type RootConfig struct {
|
||||
// Copyright information.
|
||||
Copyright string
|
||||
|
||||
// The language to apply to content without any Clolanguage indicator.
|
||||
// The language to apply to content without any language indicator.
|
||||
DefaultContentLanguage string
|
||||
|
||||
// By defefault, we put the default content language in the root and the others below their language ID, e.g. /no/.
|
||||
// By default, we put the default content language in the root and the others below their language ID, e.g. /no/.
|
||||
// Set this to true to put all languages below their language ID.
|
||||
DefaultContentLanguageInSubdir bool
|
||||
|
||||
@@ -468,11 +492,8 @@ type RootConfig struct {
|
||||
// Enable to print greppable placeholders (on the form "[i18n] TRANSLATIONID") for missing translation strings.
|
||||
EnableMissingTranslationPlaceholders bool
|
||||
|
||||
// Enable to print warnings for missing translation strings.
|
||||
LogI18nWarnings bool
|
||||
|
||||
// ENable to print warnings for multiple files published to the same destination.
|
||||
LogPathWarnings bool
|
||||
// Enable to panic on warning log entries. This may make it easier to detect the source.
|
||||
PanicOnWarning bool
|
||||
|
||||
// The configured environment. Default is "development" for server and "production" for build.
|
||||
Environment string
|
||||
@@ -506,6 +527,12 @@ type RootConfig struct {
|
||||
// Whether to track and print unused templates during the build.
|
||||
PrintUnusedTemplates bool
|
||||
|
||||
// Enable to print warnings for missing translation strings.
|
||||
PrintI18nWarnings bool
|
||||
|
||||
// ENable to print warnings for multiple files published to the same destination.
|
||||
PrintPathWarnings bool
|
||||
|
||||
// URL to be used as a placeholder when a page reference cannot be found in ref or relref. Is used as-is.
|
||||
RefLinksNotFoundURL string
|
||||
|
||||
@@ -526,7 +553,7 @@ type RootConfig struct {
|
||||
// See Modules for more a more flexible way to load themes.
|
||||
Theme []string
|
||||
|
||||
// Timeout for generating page contents, specified as a duration or in milliseconds.
|
||||
// Timeout for generating page contents, specified as a duration or in seconds.
|
||||
Timeout string
|
||||
|
||||
// The time zone (or location), e.g. Europe/Oslo, used to parse front matter dates without such information and in the time function.
|
||||
@@ -649,6 +676,26 @@ func (c *Configs) Init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// We should consolidate this, but to get a full view of the mounts in e.g. "hugo config" we need to
|
||||
// transfer any default mounts added above to the config used to print the config.
|
||||
for _, m := range c.Modules[0].Mounts() {
|
||||
var found bool
|
||||
for _, cm := range c.Base.Module.Mounts {
|
||||
if cm.Source == m.Source && cm.Target == m.Target && cm.Lang == m.Lang {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
c.Base.Module.Mounts = append(c.Base.Module.Mounts, m)
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer the changed mounts to the language versions (all share the same mount set, but can be displayed in different languages).
|
||||
for _, l := range c.LanguageConfigSlice {
|
||||
l.Module.Mounts = c.Base.Module.Mounts
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -729,8 +776,8 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
isMultiHost = true
|
||||
}
|
||||
mergedConfig.Set(kk, vv)
|
||||
if cfg.IsSet(kk) {
|
||||
rootv := cfg.Get(kk)
|
||||
rootv := cfg.Get(kk)
|
||||
if rootv != nil && cfg.IsSet(kk) {
|
||||
// This overrides a root key and potentially needs a merge.
|
||||
if !reflect.DeepEqual(rootv, vv) {
|
||||
switch vvv := vv.(type) {
|
||||
@@ -767,12 +814,14 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
|
||||
// Create a copy of the complete config and replace the root keys with the language specific ones.
|
||||
clone := all.cloneForLang()
|
||||
|
||||
if err := decodeConfigFromParams(fs, bcfg, mergedConfig, clone, differentRootKeys); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode config for language %q: %w", k, err)
|
||||
}
|
||||
if err := clone.CompileConfig(logger); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
langConfigMap[k] = clone
|
||||
case maps.ParamsMergeStrategy:
|
||||
default:
|
||||
|
||||
@@ -60,7 +60,14 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
key: "",
|
||||
weight: -100, // Always first.
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
return mapstructure.WeakDecode(p.p.Get(""), &p.c.RootConfig)
|
||||
if err := mapstructure.WeakDecode(p.p.Get(""), &p.c.RootConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This need to match with Lang which is always lower case.
|
||||
p.c.RootConfig.DefaultContentLanguage = strings.ToLower(p.c.RootConfig.DefaultContentLanguage)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
"imaging": {
|
||||
@@ -131,8 +138,8 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
return err
|
||||
},
|
||||
},
|
||||
"mediaTypes": {
|
||||
key: "mediaTypes",
|
||||
"mediatypes": {
|
||||
key: "mediatypes",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
var err error
|
||||
p.c.MediaTypes, err = media.DecodeTypes(p.p.GetStringMap(d.key))
|
||||
@@ -143,7 +150,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
key: "outputs",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
defaults := createDefaultOutputFormats(p.c.OutputFormats.Config)
|
||||
m := p.p.GetStringMap("outputs")
|
||||
m := maps.CleanConfigStringMap(p.p.GetStringMap("outputs"))
|
||||
p.c.Outputs = make(map[string][]string)
|
||||
for k, v := range m {
|
||||
s := types.ToStringSlicePreserveString(v)
|
||||
@@ -161,8 +168,8 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
return nil
|
||||
},
|
||||
},
|
||||
"outputFormats": {
|
||||
key: "outputFormats",
|
||||
"outputformats": {
|
||||
key: "outputformats",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
var err error
|
||||
p.c.OutputFormats, err = output.DecodeConfig(p.c.MediaTypes.Config, p.p.Get(d.key))
|
||||
@@ -199,8 +206,9 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
"permalinks": {
|
||||
key: "permalinks",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
p.c.Permalinks = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
|
||||
return nil
|
||||
var err error
|
||||
p.c.Permalinks, err = page.DecodePermalinksConfig(p.p.GetStringMap(d.key))
|
||||
return err
|
||||
},
|
||||
},
|
||||
"sitemap": {
|
||||
@@ -241,8 +249,41 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
key: "languages",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
var err error
|
||||
p.c.Languages, err = langs.DecodeConfig(p.p.GetStringMap(d.key))
|
||||
return err
|
||||
m := p.p.GetStringMap(d.key)
|
||||
if len(m) == 1 {
|
||||
// In v0.112.4 we moved this to the language config, but it's very commmon for mono language sites to have this at the top level.
|
||||
var first maps.Params
|
||||
var ok bool
|
||||
for _, v := range m {
|
||||
first, ok = v.(maps.Params)
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if first != nil {
|
||||
if _, found := first["languagecode"]; !found {
|
||||
first["languagecode"] = p.p.GetString("languagecode")
|
||||
}
|
||||
}
|
||||
}
|
||||
p.c.Languages, err = langs.DecodeConfig(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate defaultContentLanguage.
|
||||
var found bool
|
||||
for lang := range p.c.Languages {
|
||||
if lang == p.c.DefaultContentLanguage {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
"cascade": {
|
||||
@@ -296,14 +337,14 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
"author": {
|
||||
key: "author",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
p.c.Author = p.p.GetStringMap(d.key)
|
||||
p.c.Author = maps.CleanConfigStringMap(p.p.GetStringMap(d.key))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
"social": {
|
||||
key: "social",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
p.c.Social = p.p.GetStringMapString(d.key)
|
||||
p.c.Social = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
@@ -329,3 +370,19 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
for k, v := range allDecoderSetups {
|
||||
// Verify that k and v.key is all lower case.
|
||||
if k != strings.ToLower(k) {
|
||||
panic(fmt.Sprintf("key %q is not lower case", k))
|
||||
}
|
||||
if v.key != strings.ToLower(v.key) {
|
||||
panic(fmt.Sprintf("key %q is not lower case", v.key))
|
||||
}
|
||||
|
||||
if k != v.key {
|
||||
panic(fmt.Sprintf("key %q is not the same as the map key %q", k, v.key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,16 @@ func (c ConfigLanguage) LanguagesDefaultFirst() langs.Languages {
|
||||
return c.m.LanguagesDefaultFirst
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) LanguagePrefix() string {
|
||||
if c.DefaultContentLanguageInSubdir() && c.DefaultContentLanguage() == c.Language().Lang {
|
||||
return c.Language().Lang
|
||||
}
|
||||
if !c.IsMultiLingual() || c.DefaultContentLanguage() == c.Language().Lang {
|
||||
return ""
|
||||
}
|
||||
return c.Language().Lang
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) BaseURL() urls.BaseURL {
|
||||
return c.config.C.BaseURL
|
||||
}
|
||||
@@ -199,8 +209,8 @@ func (c ConfigLanguage) EnableMissingTranslationPlaceholders() bool {
|
||||
return c.config.EnableMissingTranslationPlaceholders
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) LogI18nWarnings() bool {
|
||||
return c.config.LogI18nWarnings
|
||||
func (c ConfigLanguage) PrintI18nWarnings() bool {
|
||||
return c.config.PrintI18nWarnings
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) CreateTitle(s string) string {
|
||||
|
||||
@@ -60,7 +60,7 @@ Title: {{ .Title }}
|
||||
|
||||
b.Assert(enConcp.BaseURL().String(), qt.Equals, "https://example.com")
|
||||
modConf := enConf.Module
|
||||
b.Assert(modConf.Mounts, qt.HasLen, 2)
|
||||
b.Assert(modConf.Mounts, qt.HasLen, 8)
|
||||
b.Assert(modConf.Mounts[0].Source, qt.Equals, filepath.FromSlash("content/en"))
|
||||
b.Assert(modConf.Mounts[0].Target, qt.Equals, "content")
|
||||
b.Assert(modConf.Mounts[0].Lang, qt.Equals, "en")
|
||||
@@ -69,3 +69,21 @@ Title: {{ .Title }}
|
||||
b.Assert(modConf.Mounts[1].Lang, qt.Equals, "sv")
|
||||
|
||||
}
|
||||
|
||||
func TestConfigAliases(t *testing.T) {
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
logI18nWarnings = true
|
||||
logPathWarnings = true
|
||||
`
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{T: t, TxtarString: files},
|
||||
).Build()
|
||||
|
||||
conf := b.H.Configs.Base
|
||||
|
||||
b.Assert(conf.PrintI18nWarnings, qt.Equals, true)
|
||||
b.Assert(conf.PrintPathWarnings, qt.Equals, true)
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
hglob "github.com/gohugoio/hugo/hugofs/glob"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
@@ -45,7 +46,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
|
||||
}
|
||||
|
||||
if d.Logger == nil {
|
||||
d.Logger = loggers.NewErrorLogger()
|
||||
d.Logger = loggers.NewDefault()
|
||||
}
|
||||
|
||||
l := &configLoader{ConfigSourceDescriptor: d, cfg: config.New()}
|
||||
@@ -89,6 +90,10 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
|
||||
return nil, fmt.Errorf("failed to init config: %w", err)
|
||||
}
|
||||
|
||||
// This is unfortunate, but these are global settings.
|
||||
tpl.SetSecurityAllowActionJSTmpl(configs.Base.Security.GoTemplates.AllowActionJSTmpl)
|
||||
loggers.InitGlobalLogger(configs.Base.PanicOnWarning)
|
||||
|
||||
return configs, nil
|
||||
|
||||
}
|
||||
@@ -134,7 +139,11 @@ type configLoader struct {
|
||||
|
||||
// Handle some legacy values.
|
||||
func (l configLoader) applyConfigAliases() error {
|
||||
aliases := []types.KeyValueStr{{Key: "taxonomies", Value: "indexes"}}
|
||||
aliases := []types.KeyValueStr{
|
||||
{Key: "indexes", Value: "taxonomies"},
|
||||
{Key: "logI18nWarnings", Value: "printI18nWarnings"},
|
||||
{Key: "logPathWarnings", Value: "printPathWarnings"},
|
||||
}
|
||||
|
||||
for _, alias := range aliases {
|
||||
if l.cfg.IsSet(alias.Key) {
|
||||
@@ -284,11 +293,19 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error {
|
||||
} else {
|
||||
l.cfg.Set(env.Key, val)
|
||||
}
|
||||
} else if nestedKey != "" {
|
||||
owner[nestedKey] = env.Value
|
||||
} else {
|
||||
// The container does not exist yet.
|
||||
l.cfg.Set(strings.ReplaceAll(env.Key, delim, "."), env.Value)
|
||||
if nestedKey != "" {
|
||||
owner[nestedKey] = env.Value
|
||||
} else {
|
||||
var val any = env.Value
|
||||
if _, ok := allDecoderSetups[env.Key]; ok {
|
||||
// A map.
|
||||
val, err = metadecoders.Default.UnmarshalStringTo(env.Value, map[string]interface{}{})
|
||||
}
|
||||
if err == nil {
|
||||
l.cfg.Set(strings.ReplaceAll(env.Key, delim, "."), val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-8
@@ -19,6 +19,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
@@ -81,7 +82,7 @@ type LoadConfigResult struct {
|
||||
|
||||
var defaultBuild = BuildConfig{
|
||||
UseResourceCacheWhen: "fallback",
|
||||
WriteStats: false,
|
||||
BuildStats: BuildStats{},
|
||||
|
||||
CacheBusters: []CacheBuster{
|
||||
{
|
||||
@@ -96,7 +97,7 @@ var defaultBuild = BuildConfig{
|
||||
Source: `(postcss|tailwind)\.config\.js`,
|
||||
Target: cssTargetCachebusterRe,
|
||||
},
|
||||
// This is deliberatly coarse grained; it will cache bust resources with "json" in the cache key when js files changes, which is good.
|
||||
// This is deliberately coarse grained; it will cache bust resources with "json" in the cache key when js files changes, which is good.
|
||||
{
|
||||
Source: `assets/.*\.(.*)$`,
|
||||
Target: `$1`,
|
||||
@@ -110,9 +111,10 @@ type BuildConfig struct {
|
||||
|
||||
// When enabled, will collect and write a hugo_stats.json with some build
|
||||
// related aggregated data (e.g. CSS class names).
|
||||
WriteStats bool
|
||||
// Note that this was a bool <= v0.115.0.
|
||||
BuildStats BuildStats
|
||||
|
||||
// Can be used to toggle off writing of the intellinsense /assets/jsconfig.js
|
||||
// Can be used to toggle off writing of the IntelliSense /assets/jsconfig.js
|
||||
// file.
|
||||
NoJSConfigInAssets bool
|
||||
|
||||
@@ -120,6 +122,21 @@ type BuildConfig struct {
|
||||
CacheBusters []CacheBuster
|
||||
}
|
||||
|
||||
// BuildStats configures if and what to write to the hugo_stats.json file.
|
||||
type BuildStats struct {
|
||||
Enable bool
|
||||
DisableTags bool
|
||||
DisableClasses bool
|
||||
DisableIDs bool
|
||||
}
|
||||
|
||||
func (w BuildStats) Enabled() bool {
|
||||
if !w.Enable {
|
||||
return false
|
||||
}
|
||||
return !w.DisableTags || !w.DisableClasses || !w.DisableIDs
|
||||
}
|
||||
|
||||
func (b BuildConfig) clone() BuildConfig {
|
||||
b.CacheBusters = append([]CacheBuster{}, b.CacheBusters...)
|
||||
return b
|
||||
@@ -131,7 +148,7 @@ func (b BuildConfig) UseResourceCache(err error) bool {
|
||||
}
|
||||
|
||||
if b.UseResourceCacheWhen == "fallback" {
|
||||
return err == herrors.ErrFeatureNotAvailable
|
||||
return herrors.IsFeatureNotAvailableError(err)
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -170,14 +187,22 @@ func (b *BuildConfig) CompileConfig(logger loggers.Logger) error {
|
||||
|
||||
func DecodeBuildConfig(cfg Provider) BuildConfig {
|
||||
m := cfg.GetStringMap("build")
|
||||
|
||||
b := defaultBuild.clone()
|
||||
if m == nil {
|
||||
return b
|
||||
}
|
||||
|
||||
// writeStats was a bool <= v0.115.0.
|
||||
if writeStats, ok := m["writestats"]; ok {
|
||||
if bb, ok := writeStats.(bool); ok {
|
||||
m["buildstats"] = BuildStats{Enable: bb}
|
||||
}
|
||||
}
|
||||
|
||||
err := mapstructure.WeakDecode(m, &b)
|
||||
if err != nil {
|
||||
return defaultBuild
|
||||
return b
|
||||
}
|
||||
|
||||
b.UseResourceCacheWhen = strings.ToLower(b.UseResourceCacheWhen)
|
||||
@@ -306,6 +331,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
|
||||
if c.compiledSource != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
source := c.Source
|
||||
target := c.Target
|
||||
sourceRe, err := regexp.Compile(source)
|
||||
@@ -313,6 +339,8 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
|
||||
return fmt.Errorf("failed to compile cache buster source %q: %w", c.Source, err)
|
||||
}
|
||||
var compileErr error
|
||||
debugl := logger.Logger().WithLevel(logg.LevelDebug).WithField(loggers.FieldNameCmd, "cachebuster")
|
||||
|
||||
c.compiledSource = func(s string) func(string) bool {
|
||||
m := sourceRe.FindStringSubmatch(s)
|
||||
matchString := "no match"
|
||||
@@ -320,7 +348,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
|
||||
if match {
|
||||
matchString = "match!"
|
||||
}
|
||||
logger.Debugf("cachebuster: Matching %q with source %q: %s\n", s, source, matchString)
|
||||
debugl.Logf("Matching %q with source %q: %s", s, source, matchString)
|
||||
if !match {
|
||||
return nil
|
||||
}
|
||||
@@ -341,7 +369,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
|
||||
if match {
|
||||
matchString = "match!"
|
||||
}
|
||||
logger.Debugf("cachebuster: Matching %q with target %q: %s\n", s, target, matchString)
|
||||
logger.Debugf("Matching %q with target %q: %s", s, target, matchString)
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ status = 301
|
||||
|
||||
s, err := DecodeServer(cfg)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(s.CompileConfig(loggers.NewErrorLogger()), qt.IsNil)
|
||||
c.Assert(s.CompileConfig(loggers.NewDefault()), qt.IsNil)
|
||||
|
||||
c.Assert(s.MatchHeaders("/foo.jpg"), qt.DeepEquals, []types.KeyValueStr{
|
||||
{Key: "X-Content-Type-Options", Value: "nosniff"},
|
||||
@@ -145,7 +145,7 @@ func TestBuildConfigCacheBusters(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := New()
|
||||
conf := DecodeBuildConfig(cfg)
|
||||
l := loggers.NewInfoLogger()
|
||||
l := loggers.NewDefault()
|
||||
c.Assert(conf.CompileConfig(l), qt.IsNil)
|
||||
|
||||
m, err := conf.MatchCacheBuster(l, "assets/foo/main.js")
|
||||
|
||||
@@ -157,7 +157,7 @@ func LoadConfigFromDir(sourceFs afero.Fs, configDir, environment string) (Provid
|
||||
if err != nil {
|
||||
// This will be used in error reporting, use the most specific value.
|
||||
dirnames = []string{path}
|
||||
return fmt.Errorf("failed to unmarshl config for path %q: %w", path, err)
|
||||
return fmt.Errorf("failed to unmarshal config for path %q: %w", path, err)
|
||||
}
|
||||
|
||||
var keyPath []string
|
||||
|
||||
@@ -27,6 +27,7 @@ type AllProvider interface {
|
||||
Language() *langs.Language
|
||||
Languages() langs.Languages
|
||||
LanguagesDefaultFirst() langs.Languages
|
||||
LanguagePrefix() string
|
||||
BaseURL() urls.BaseURL
|
||||
BaseURLLiveReload() urls.BaseURL
|
||||
Environment() string
|
||||
@@ -57,7 +58,7 @@ type AllProvider interface {
|
||||
EnableMissingTranslationPlaceholders() bool
|
||||
TemplateMetrics() bool
|
||||
TemplateMetricsHints() bool
|
||||
LogI18nWarnings() bool
|
||||
PrintI18nWarnings() bool
|
||||
CreateTitle(s string) string
|
||||
IgnoreFile(s string) bool
|
||||
NewContentEditor() string
|
||||
|
||||
@@ -34,22 +34,22 @@ const securityConfigKey = "security"
|
||||
// DefaultConfig holds the default security policy.
|
||||
var DefaultConfig = Config{
|
||||
Exec: Exec{
|
||||
Allow: NewWhitelist(
|
||||
"^dart-sass-embedded$",
|
||||
"^go$", // for Go Modules
|
||||
"^npx$", // used by all Node tools (Babel, PostCSS).
|
||||
Allow: MustNewWhitelist(
|
||||
"^(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded.
|
||||
"^go$", // for Go Modules
|
||||
"^npx$", // used by all Node tools (Babel, PostCSS).
|
||||
"^postcss$",
|
||||
),
|
||||
// These have been tested to work with Hugo's external programs
|
||||
// on Windows, Linux and MacOS.
|
||||
OsEnv: NewWhitelist(`(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+)$`),
|
||||
OsEnv: MustNewWhitelist(`(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG)$`),
|
||||
},
|
||||
Funcs: Funcs{
|
||||
Getenv: NewWhitelist("^HUGO_", "^CI$"),
|
||||
Getenv: MustNewWhitelist("^HUGO_", "^CI$"),
|
||||
},
|
||||
HTTP: HTTP{
|
||||
URLs: NewWhitelist(".*"),
|
||||
Methods: NewWhitelist("(?i)GET|POST"),
|
||||
URLs: MustNewWhitelist(".*"),
|
||||
Methods: MustNewWhitelist("(?i)GET|POST"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -68,6 +68,9 @@ type Config struct {
|
||||
|
||||
// Allow inline shortcodes
|
||||
EnableInlineShortcodes bool `json:"enableInlineShortcodes"`
|
||||
|
||||
// Go templates related security config.
|
||||
GoTemplates GoTemplates `json:"goTemplates"`
|
||||
}
|
||||
|
||||
// Exec holds os/exec policies.
|
||||
@@ -93,6 +96,15 @@ type HTTP struct {
|
||||
MediaTypes Whitelist `json:"mediaTypes"`
|
||||
}
|
||||
|
||||
type GoTemplates struct {
|
||||
|
||||
// Enable to allow template actions inside bakcticks in ES6 template literals.
|
||||
// This was blocked in Hugo 0.114.0 for security reasons and you now get errors on the form
|
||||
// "... appears in a JS template literal" if you have this in your templates.
|
||||
// See https://github.com/golang/go/issues/59234
|
||||
AllowActionJSTmpl bool
|
||||
}
|
||||
|
||||
// ToTOML converts c to TOML with [security] as the root.
|
||||
func (c Config) ToTOML() string {
|
||||
sec := c.ToSecurityMap()
|
||||
@@ -209,7 +221,7 @@ func stringSliceToWhitelistHook() mapstructure.DecodeHookFuncType {
|
||||
|
||||
wl := types.ToStringSlicePreserveString(data)
|
||||
|
||||
return NewWhitelist(wl...), nil
|
||||
return NewWhitelist(wl...)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func TestToTOML(t *testing.T) {
|
||||
got := DefaultConfig.ToTOML()
|
||||
|
||||
c.Assert(got, qt.Equals,
|
||||
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.goTemplates]\n AllowActionJSTmpl = false\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -154,9 +154,6 @@ func TestDecodeConfigDefault(t *testing.T) {
|
||||
c.Assert(pc.Exec.Allow.Accept("a"), qt.IsFalse)
|
||||
c.Assert(pc.Exec.Allow.Accept("npx"), qt.IsTrue)
|
||||
c.Assert(pc.Exec.Allow.Accept("Npx"), qt.IsFalse)
|
||||
c.Assert(pc.Exec.OsEnv.Accept("a"), qt.IsFalse)
|
||||
c.Assert(pc.Exec.OsEnv.Accept("PATH"), qt.IsTrue)
|
||||
c.Assert(pc.Exec.OsEnv.Accept("e"), qt.IsFalse)
|
||||
|
||||
c.Assert(pc.HTTP.URLs.Accept("https://example.org"), qt.IsTrue)
|
||||
c.Assert(pc.HTTP.Methods.Accept("POST"), qt.IsTrue)
|
||||
@@ -167,6 +164,10 @@ func TestDecodeConfigDefault(t *testing.T) {
|
||||
|
||||
c.Assert(pc.Exec.OsEnv.Accept("PATH"), qt.IsTrue)
|
||||
c.Assert(pc.Exec.OsEnv.Accept("GOROOT"), qt.IsTrue)
|
||||
c.Assert(pc.Exec.OsEnv.Accept("HOME"), qt.IsTrue)
|
||||
c.Assert(pc.Exec.OsEnv.Accept("SSH_AUTH_SOCK"), qt.IsTrue)
|
||||
c.Assert(pc.Exec.OsEnv.Accept("a"), qt.IsFalse)
|
||||
c.Assert(pc.Exec.OsEnv.Accept("e"), qt.IsFalse)
|
||||
c.Assert(pc.Exec.OsEnv.Accept("MYSECRET"), qt.IsFalse)
|
||||
|
||||
}
|
||||
|
||||
@@ -45,9 +45,9 @@ func (w Whitelist) MarshalJSON() ([]byte, error) {
|
||||
// NewWhitelist creates a new Whitelist from zero or more patterns.
|
||||
// An empty patterns list or a pattern with the value 'none' will create
|
||||
// a whitelist that will Accept none.
|
||||
func NewWhitelist(patterns ...string) Whitelist {
|
||||
func NewWhitelist(patterns ...string) (Whitelist, error) {
|
||||
if len(patterns) == 0 {
|
||||
return Whitelist{acceptNone: true}
|
||||
return Whitelist{acceptNone: true}, nil
|
||||
}
|
||||
|
||||
var acceptSome bool
|
||||
@@ -68,7 +68,7 @@ func NewWhitelist(patterns ...string) Whitelist {
|
||||
if !acceptSome {
|
||||
return Whitelist{
|
||||
acceptNone: true,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
var patternsr []*regexp.Regexp
|
||||
@@ -78,10 +78,23 @@ func NewWhitelist(patterns ...string) Whitelist {
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
patternsr = append(patternsr, regexp.MustCompile(p))
|
||||
re, err := regexp.Compile(p)
|
||||
if err != nil {
|
||||
return Whitelist{}, fmt.Errorf("failed to compile whitelist pattern %q: %w", p, err)
|
||||
}
|
||||
patternsr = append(patternsr, re)
|
||||
}
|
||||
|
||||
return Whitelist{patterns: patternsr, patternsStrings: patternsStrings}
|
||||
return Whitelist{patterns: patternsr, patternsStrings: patternsStrings}, nil
|
||||
}
|
||||
|
||||
// MustNewWhitelist creates a new Whitelist from zero or more patterns and panics on error.
|
||||
func MustNewWhitelist(patterns ...string) Whitelist {
|
||||
w, err := NewWhitelist(patterns...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// Accept reports whether name is whitelisted.
|
||||
|
||||
@@ -24,21 +24,21 @@ func TestWhitelist(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Run("none", func(c *qt.C) {
|
||||
c.Assert(NewWhitelist("none", "foo").Accept("foo"), qt.IsFalse)
|
||||
c.Assert(NewWhitelist().Accept("foo"), qt.IsFalse)
|
||||
c.Assert(NewWhitelist("").Accept("foo"), qt.IsFalse)
|
||||
c.Assert(NewWhitelist(" ", " ").Accept("foo"), qt.IsFalse)
|
||||
c.Assert(MustNewWhitelist("none", "foo").Accept("foo"), qt.IsFalse)
|
||||
c.Assert(MustNewWhitelist().Accept("foo"), qt.IsFalse)
|
||||
c.Assert(MustNewWhitelist("").Accept("foo"), qt.IsFalse)
|
||||
c.Assert(MustNewWhitelist(" ", " ").Accept("foo"), qt.IsFalse)
|
||||
c.Assert(Whitelist{}.Accept("foo"), qt.IsFalse)
|
||||
})
|
||||
|
||||
c.Run("One", func(c *qt.C) {
|
||||
w := NewWhitelist("^foo.*")
|
||||
w := MustNewWhitelist("^foo.*")
|
||||
c.Assert(w.Accept("foo"), qt.IsTrue)
|
||||
c.Assert(w.Accept("mfoo"), qt.IsFalse)
|
||||
})
|
||||
|
||||
c.Run("Multiple", func(c *qt.C) {
|
||||
w := NewWhitelist("^foo.*", "^bar.*")
|
||||
w := MustNewWhitelist("^foo.*", "^bar.*")
|
||||
c.Assert(w.Accept("foo"), qt.IsTrue)
|
||||
c.Assert(w.Accept("bar"), qt.IsTrue)
|
||||
c.Assert(w.Accept("mbar"), qt.IsFalse)
|
||||
|
||||
@@ -41,7 +41,7 @@ func GetTestConfigs(fs afero.Fs, cfg config.Provider) *allconfig.Configs {
|
||||
}
|
||||
}
|
||||
|
||||
configs, err := allconfig.LoadConfig(allconfig.ConfigSourceDescriptor{Fs: fs, Flags: cfg})
|
||||
configs, err := allconfig.LoadConfig(allconfig.ConfigSourceDescriptor{Fs: fs, Flags: cfg, Environ: []string{"EMPTY_TEST_ENVIRONMENT"}})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
+43
-38
@@ -37,10 +37,10 @@ import (
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/spf13/afero"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
|
||||
"gocloud.dev/blob"
|
||||
@@ -56,9 +56,10 @@ type Deployer struct {
|
||||
bucket *blob.Bucket
|
||||
|
||||
mediaTypes media.Types // Hugo's MediaType to guess ContentType
|
||||
quiet bool // true reduces STDOUT
|
||||
quiet bool // true reduces STDOUT // TODO(bep) remove, this is a global feature.
|
||||
|
||||
cfg DeployConfig
|
||||
cfg DeployConfig
|
||||
logger loggers.Logger
|
||||
|
||||
target *Target // the target to deploy to
|
||||
|
||||
@@ -73,7 +74,7 @@ type deploySummary struct {
|
||||
const metaMD5Hash = "md5chksum" // the meta key to store md5hash in
|
||||
|
||||
// New constructs a new *Deployer.
|
||||
func New(cfg config.AllProvider, localFs afero.Fs) (*Deployer, error) {
|
||||
func New(cfg config.AllProvider, logger loggers.Logger, localFs afero.Fs) (*Deployer, error) {
|
||||
|
||||
dcfg := cfg.GetConfigSection(deploymentConfigKey).(DeployConfig)
|
||||
targetName := dcfg.Target
|
||||
@@ -112,12 +113,16 @@ func (d *Deployer) openBucket(ctx context.Context) (*blob.Bucket, error) {
|
||||
if d.bucket != nil {
|
||||
return d.bucket, nil
|
||||
}
|
||||
jww.FEEDBACK.Printf("Deploying to target %q (%s)\n", d.target.Name, d.target.URL)
|
||||
d.logger.Printf("Deploying to target %q (%s)\n", d.target.Name, d.target.URL)
|
||||
return blob.OpenBucket(ctx, d.target.URL)
|
||||
}
|
||||
|
||||
// Deploy deploys the site to a target.
|
||||
func (d *Deployer) Deploy(ctx context.Context) error {
|
||||
if d.logger == nil {
|
||||
d.logger = loggers.NewDefault()
|
||||
}
|
||||
|
||||
bucket, err := d.openBucket(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -132,33 +137,33 @@ func (d *Deployer) Deploy(ctx context.Context) error {
|
||||
if d.target != nil {
|
||||
include, exclude = d.target.includeGlob, d.target.excludeGlob
|
||||
}
|
||||
local, err := walkLocal(d.localFs, d.cfg.Matchers, include, exclude, d.mediaTypes)
|
||||
local, err := d.walkLocal(d.localFs, d.cfg.Matchers, include, exclude, d.mediaTypes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
jww.INFO.Printf("Found %d local files.\n", len(local))
|
||||
d.logger.Infof("Found %d local files.\n", len(local))
|
||||
d.summary.NumLocal = len(local)
|
||||
|
||||
// Load remote files from the target.
|
||||
remote, err := walkRemote(ctx, bucket, include, exclude)
|
||||
remote, err := d.walkRemote(ctx, bucket, include, exclude)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
jww.INFO.Printf("Found %d remote files.\n", len(remote))
|
||||
d.logger.Infof("Found %d remote files.\n", len(remote))
|
||||
d.summary.NumRemote = len(remote)
|
||||
|
||||
// Diff local vs remote to see what changes need to be applied.
|
||||
uploads, deletes := findDiffs(local, remote, d.cfg.Force)
|
||||
uploads, deletes := d.findDiffs(local, remote, d.cfg.Force)
|
||||
d.summary.NumUploads = len(uploads)
|
||||
d.summary.NumDeletes = len(deletes)
|
||||
if len(uploads)+len(deletes) == 0 {
|
||||
if !d.quiet {
|
||||
jww.FEEDBACK.Println("No changes required.")
|
||||
d.logger.Println("No changes required.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !d.quiet {
|
||||
jww.FEEDBACK.Println(summarizeChanges(uploads, deletes))
|
||||
d.logger.Println(summarizeChanges(uploads, deletes))
|
||||
}
|
||||
|
||||
// Ask for confirmation before proceeding.
|
||||
@@ -192,14 +197,14 @@ func (d *Deployer) Deploy(ctx context.Context) error {
|
||||
for _, upload := range uploads {
|
||||
if d.cfg.DryRun {
|
||||
if !d.quiet {
|
||||
jww.FEEDBACK.Printf("[DRY RUN] Would upload: %v\n", upload)
|
||||
d.logger.Printf("[DRY RUN] Would upload: %v\n", upload)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
sem <- struct{}{}
|
||||
go func(upload *fileToUpload) {
|
||||
if err := doSingleUpload(ctx, bucket, upload); err != nil {
|
||||
if err := d.doSingleUpload(ctx, bucket, upload); err != nil {
|
||||
errMu.Lock()
|
||||
defer errMu.Unlock()
|
||||
errs = append(errs, err)
|
||||
@@ -214,7 +219,7 @@ func (d *Deployer) Deploy(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if d.cfg.MaxDeletes != -1 && len(deletes) > d.cfg.MaxDeletes {
|
||||
jww.WARN.Printf("Skipping %d deletes because it is more than --maxDeletes (%d). If this is expected, set --maxDeletes to a larger number, or -1 to disable this check.\n", len(deletes), d.cfg.MaxDeletes)
|
||||
d.logger.Warnf("Skipping %d deletes because it is more than --maxDeletes (%d). If this is expected, set --maxDeletes to a larger number, or -1 to disable this check.\n", len(deletes), d.cfg.MaxDeletes)
|
||||
d.summary.NumDeletes = 0
|
||||
} else {
|
||||
// Apply deletes in parallel.
|
||||
@@ -223,16 +228,16 @@ func (d *Deployer) Deploy(ctx context.Context) error {
|
||||
for _, del := range deletes {
|
||||
if d.cfg.DryRun {
|
||||
if !d.quiet {
|
||||
jww.FEEDBACK.Printf("[DRY RUN] Would delete %s\n", del)
|
||||
d.logger.Printf("[DRY RUN] Would delete %s\n", del)
|
||||
}
|
||||
continue
|
||||
}
|
||||
sem <- struct{}{}
|
||||
go func(del string) {
|
||||
jww.INFO.Printf("Deleting %s...\n", del)
|
||||
d.logger.Infof("Deleting %s...\n", del)
|
||||
if err := bucket.Delete(ctx, del); err != nil {
|
||||
if gcerrors.Code(err) == gcerrors.NotFound {
|
||||
jww.WARN.Printf("Failed to delete %q because it wasn't found: %v", del, err)
|
||||
d.logger.Warnf("Failed to delete %q because it wasn't found: %v", del, err)
|
||||
} else {
|
||||
errMu.Lock()
|
||||
defer errMu.Unlock()
|
||||
@@ -250,24 +255,24 @@ func (d *Deployer) Deploy(ctx context.Context) error {
|
||||
|
||||
if len(errs) > 0 {
|
||||
if !d.quiet {
|
||||
jww.FEEDBACK.Printf("Encountered %d errors.\n", len(errs))
|
||||
d.logger.Printf("Encountered %d errors.\n", len(errs))
|
||||
}
|
||||
return errs[0]
|
||||
}
|
||||
if !d.quiet {
|
||||
jww.FEEDBACK.Println("Success!")
|
||||
d.logger.Println("Success!")
|
||||
}
|
||||
|
||||
if d.cfg.InvalidateCDN {
|
||||
if d.target.CloudFrontDistributionID != "" {
|
||||
if d.cfg.DryRun {
|
||||
if !d.quiet {
|
||||
jww.FEEDBACK.Printf("[DRY RUN] Would invalidate CloudFront CDN with ID %s\n", d.target.CloudFrontDistributionID)
|
||||
d.logger.Printf("[DRY RUN] Would invalidate CloudFront CDN with ID %s\n", d.target.CloudFrontDistributionID)
|
||||
}
|
||||
} else {
|
||||
jww.FEEDBACK.Println("Invalidating CloudFront CDN...")
|
||||
d.logger.Println("Invalidating CloudFront CDN...")
|
||||
if err := InvalidateCloudFront(ctx, d.target.CloudFrontDistributionID); err != nil {
|
||||
jww.FEEDBACK.Printf("Failed to invalidate CloudFront CDN: %v\n", err)
|
||||
d.logger.Printf("Failed to invalidate CloudFront CDN: %v\n", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -275,17 +280,17 @@ func (d *Deployer) Deploy(ctx context.Context) error {
|
||||
if d.target.GoogleCloudCDNOrigin != "" {
|
||||
if d.cfg.DryRun {
|
||||
if !d.quiet {
|
||||
jww.FEEDBACK.Printf("[DRY RUN] Would invalidate Google Cloud CDN with origin %s\n", d.target.GoogleCloudCDNOrigin)
|
||||
d.logger.Printf("[DRY RUN] Would invalidate Google Cloud CDN with origin %s\n", d.target.GoogleCloudCDNOrigin)
|
||||
}
|
||||
} else {
|
||||
jww.FEEDBACK.Println("Invalidating Google Cloud CDN...")
|
||||
d.logger.Println("Invalidating Google Cloud CDN...")
|
||||
if err := InvalidateGoogleCloudCDN(ctx, d.target.GoogleCloudCDNOrigin); err != nil {
|
||||
jww.FEEDBACK.Printf("Failed to invalidate Google Cloud CDN: %v\n", err)
|
||||
d.logger.Printf("Failed to invalidate Google Cloud CDN: %v\n", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
jww.FEEDBACK.Println("Success!")
|
||||
d.logger.Println("Success!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -300,8 +305,8 @@ func summarizeChanges(uploads []*fileToUpload, deletes []string) string {
|
||||
}
|
||||
|
||||
// doSingleUpload executes a single file upload.
|
||||
func doSingleUpload(ctx context.Context, bucket *blob.Bucket, upload *fileToUpload) error {
|
||||
jww.INFO.Printf("Uploading %v...\n", upload)
|
||||
func (d *Deployer) doSingleUpload(ctx context.Context, bucket *blob.Bucket, upload *fileToUpload) error {
|
||||
d.logger.Infof("Uploading %v...\n", upload)
|
||||
opts := &blob.WriterOptions{
|
||||
CacheControl: upload.Local.CacheControl(),
|
||||
ContentEncoding: upload.Local.ContentEncoding(),
|
||||
@@ -479,7 +484,7 @@ func knownHiddenDirectory(name string) bool {
|
||||
|
||||
// walkLocal walks the source directory and returns a flat list of files,
|
||||
// using localFile.SlashPath as the map keys.
|
||||
func walkLocal(fs afero.Fs, matchers []*Matcher, include, exclude glob.Glob, mediaTypes media.Types) (map[string]*localFile, error) {
|
||||
func (d *Deployer) walkLocal(fs afero.Fs, matchers []*Matcher, include, exclude glob.Glob, mediaTypes media.Types) (map[string]*localFile, error) {
|
||||
retval := map[string]*localFile{}
|
||||
err := afero.Walk(fs, "", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
@@ -509,11 +514,11 @@ func walkLocal(fs afero.Fs, matchers []*Matcher, include, exclude glob.Glob, med
|
||||
// Check include/exclude matchers.
|
||||
slashpath := filepath.ToSlash(path)
|
||||
if include != nil && !include.Match(slashpath) {
|
||||
jww.INFO.Printf(" dropping %q due to include\n", slashpath)
|
||||
d.logger.Infof(" dropping %q due to include\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
if exclude != nil && exclude.Match(slashpath) {
|
||||
jww.INFO.Printf(" dropping %q due to exclude\n", slashpath)
|
||||
d.logger.Infof(" dropping %q due to exclude\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -539,7 +544,7 @@ func walkLocal(fs afero.Fs, matchers []*Matcher, include, exclude glob.Glob, med
|
||||
}
|
||||
|
||||
// walkRemote walks the target bucket and returns a flat list.
|
||||
func walkRemote(ctx context.Context, bucket *blob.Bucket, include, exclude glob.Glob) (map[string]*blob.ListObject, error) {
|
||||
func (d *Deployer) walkRemote(ctx context.Context, bucket *blob.Bucket, include, exclude glob.Glob) (map[string]*blob.ListObject, error) {
|
||||
retval := map[string]*blob.ListObject{}
|
||||
iter := bucket.List(nil)
|
||||
for {
|
||||
@@ -552,11 +557,11 @@ func walkRemote(ctx context.Context, bucket *blob.Bucket, include, exclude glob.
|
||||
}
|
||||
// Check include/exclude matchers.
|
||||
if include != nil && !include.Match(obj.Key) {
|
||||
jww.INFO.Printf(" remote dropping %q due to include\n", obj.Key)
|
||||
d.logger.Infof(" remote dropping %q due to include\n", obj.Key)
|
||||
continue
|
||||
}
|
||||
if exclude != nil && exclude.Match(obj.Key) {
|
||||
jww.INFO.Printf(" remote dropping %q due to exclude\n", obj.Key)
|
||||
d.logger.Infof(" remote dropping %q due to exclude\n", obj.Key)
|
||||
continue
|
||||
}
|
||||
// If the remote didn't give us an MD5, use remote attributes MD5, if that doesn't exist compute one.
|
||||
@@ -629,7 +634,7 @@ func (u *fileToUpload) String() string {
|
||||
// findDiffs diffs localFiles vs remoteFiles to see what changes should be
|
||||
// applied to the remote target. It returns a slice of *fileToUpload and a
|
||||
// slice of paths for files to delete.
|
||||
func findDiffs(localFiles map[string]*localFile, remoteFiles map[string]*blob.ListObject, force bool) ([]*fileToUpload, []string) {
|
||||
func (d *Deployer) findDiffs(localFiles map[string]*localFile, remoteFiles map[string]*blob.ListObject, force bool) ([]*fileToUpload, []string) {
|
||||
var uploads []*fileToUpload
|
||||
var deletes []string
|
||||
|
||||
@@ -680,10 +685,10 @@ func findDiffs(localFiles map[string]*localFile, remoteFiles map[string]*blob.Li
|
||||
reason = reasonNotFound
|
||||
}
|
||||
if upload {
|
||||
jww.DEBUG.Printf("%s needs to be uploaded: %v\n", path, reason)
|
||||
d.logger.Debugf("%s needs to be uploaded: %v\n", path, reason)
|
||||
uploads = append(uploads, &fileToUpload{lf, reason})
|
||||
} else {
|
||||
jww.DEBUG.Printf("%s exists at target and does not need to be uploaded", path)
|
||||
d.logger.Debugf("%s exists at target and does not need to be uploaded", path)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !nodeploy
|
||||
// +build !nodeploy
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
@@ -127,11 +124,16 @@ func (m *Matcher) Matches(path string) bool {
|
||||
return m.re.MatchString(path)
|
||||
}
|
||||
|
||||
var DefaultConfig = DeployConfig{
|
||||
Workers: 10,
|
||||
InvalidateCDN: true,
|
||||
MaxDeletes: 256,
|
||||
}
|
||||
|
||||
// DecodeConfig creates a config from a given Hugo configuration.
|
||||
func DecodeConfig(cfg config.Provider) (DeployConfig, error) {
|
||||
var (
|
||||
dcfg DeployConfig
|
||||
)
|
||||
|
||||
dcfg := DefaultConfig
|
||||
|
||||
if !cfg.IsSet(deploymentConfigKey) {
|
||||
return dcfg, nil
|
||||
|
||||
+11
-2
@@ -30,6 +30,7 @@ import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
@@ -197,7 +198,8 @@ func TestFindDiffs(t *testing.T) {
|
||||
for _, r := range tc.Remote {
|
||||
remote[r.Key] = r
|
||||
}
|
||||
gotUpdates, gotDeletes := findDiffs(local, remote, tc.Force)
|
||||
d := newDeployer()
|
||||
gotUpdates, gotDeletes := d.findDiffs(local, remote, tc.Force)
|
||||
gotUpdates = applyOrdering(nil, gotUpdates)[0]
|
||||
sort.Slice(gotDeletes, func(i, j int) bool { return gotDeletes[i] < gotDeletes[j] })
|
||||
if diff := cmp.Diff(gotUpdates, tc.WantUpdates, cmpopts.IgnoreUnexported(localFile{})); diff != "" {
|
||||
@@ -249,7 +251,8 @@ func TestWalkLocal(t *testing.T) {
|
||||
fd.Close()
|
||||
}
|
||||
}
|
||||
if got, err := walkLocal(fs, nil, nil, nil, media.DefaultTypes); err != nil {
|
||||
d := newDeployer()
|
||||
if got, err := d.walkLocal(fs, nil, nil, nil, media.DefaultTypes); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
expect := map[string]any{}
|
||||
@@ -1026,3 +1029,9 @@ func verifyRemote(ctx context.Context, bucket *blob.Bucket, local []*fileData) (
|
||||
}
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
func newDeployer() *Deployer {
|
||||
return &Deployer{
|
||||
logger: loggers.NewDefault(),
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+22
-15
@@ -3,12 +3,14 @@ package deps
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
@@ -25,7 +27,6 @@ import (
|
||||
"github.com/gohugoio/hugo/source"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
"github.com/spf13/afero"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
// Deps holds dependencies used by many.
|
||||
@@ -36,9 +37,6 @@ type Deps struct {
|
||||
// The logger to use.
|
||||
Log loggers.Logger `json:"-"`
|
||||
|
||||
// Used to log errors that may repeat itself many times.
|
||||
LogDistinct loggers.Logger
|
||||
|
||||
ExecHelper *hexec.Exec
|
||||
|
||||
// The templates to use. This will usually implement the full tpl.TemplateManager.
|
||||
@@ -117,15 +115,13 @@ func (d *Deps) Init() error {
|
||||
}
|
||||
|
||||
if d.Log == nil {
|
||||
d.Log = loggers.NewErrorLogger()
|
||||
}
|
||||
|
||||
if d.LogDistinct == nil {
|
||||
d.LogDistinct = helpers.NewDistinctLogger(d.Log)
|
||||
d.Log = loggers.NewDefault()
|
||||
}
|
||||
|
||||
if d.globalErrHandler == nil {
|
||||
d.globalErrHandler = &globalErrHandler{}
|
||||
d.globalErrHandler = &globalErrHandler{
|
||||
logger: d.Log,
|
||||
}
|
||||
}
|
||||
|
||||
if d.BuildState == nil {
|
||||
@@ -189,10 +185,13 @@ func (d *Deps) Init() error {
|
||||
}
|
||||
|
||||
var common *resources.SpecCommon
|
||||
var imageCache *resources.ImageCache
|
||||
if d.ResourceSpec != nil {
|
||||
common = d.ResourceSpec.SpecCommon
|
||||
imageCache = d.ResourceSpec.ImageCache
|
||||
}
|
||||
resourceSpec, err := resources.NewSpec(d.PathSpec, common, d.BuildState, d.Log, d, d.ExecHelper)
|
||||
|
||||
resourceSpec, err := resources.NewSpec(d.PathSpec, common, imageCache, d.BuildState, d.Log, d, d.ExecHelper)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create resource spec: %w", err)
|
||||
}
|
||||
@@ -225,6 +224,8 @@ func (d *Deps) Compile(prototype *Deps) error {
|
||||
}
|
||||
|
||||
type globalErrHandler struct {
|
||||
logger loggers.Logger
|
||||
|
||||
// Channel for some "hard to get to" build errors
|
||||
buildErrors chan error
|
||||
// Used to signal that the build is done.
|
||||
@@ -243,8 +244,7 @@ func (e *globalErrHandler) SendError(err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
jww.ERROR.Println(err)
|
||||
e.logger.Errorln(err)
|
||||
}
|
||||
|
||||
func (e *globalErrHandler) StartErrorCollector() chan error {
|
||||
@@ -309,9 +309,16 @@ func (d *Deps) Close() error {
|
||||
// on a global level, i.e. logging etc.
|
||||
// Nil values will be given default values.
|
||||
type DepsCfg struct {
|
||||
// The logger to use. Only set in some tests.
|
||||
// TODO(bep) get rid of this.
|
||||
TestLogger loggers.Logger
|
||||
|
||||
// The Logger to use.
|
||||
Logger loggers.Logger
|
||||
// The logging level to use.
|
||||
LogLevel logg.Level
|
||||
|
||||
// Where to write the logs.
|
||||
// Currently we typically write everything to stdout.
|
||||
LogOut io.Writer
|
||||
|
||||
// The file systems to use
|
||||
Fs *hugofs.Fs
|
||||
|
||||
@@ -3,6 +3,3 @@
|
||||
X-XSS-Protection: 1; mode=block
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: origin-when-cross-origin
|
||||
/*
|
||||
Link: <{{ "dist/app.bundle.js" | relURL }}>; rel=preload; as=script
|
||||
Link: <{{ "dist/main.css" | relURL }}>; rel=preload; as=style
|
||||
|
||||
@@ -1 +1 @@
|
||||
# github.com/gohugoio/gohugoioTheme v0.0.0-20230418063032-99f9185b8e11
|
||||
# github.com/gohugoio/gohugoioTheme v0.0.0-20230527124826-78bc315d7b8a
|
||||
|
||||
@@ -27,9 +27,9 @@ toc: true
|
||||
|
||||
## All Privacy Settings
|
||||
|
||||
Below are all privacy settings and their default value. These settings need to be put in your site config (e.g. `config.toml`).
|
||||
Below are all privacy settings and their default value. These settings need to be put in your site config (e.g. `hugo.toml`).
|
||||
|
||||
{{< code-toggle file="config">}}
|
||||
{{< code-toggle file="hugo" >}}
|
||||
[privacy]
|
||||
[privacy.disqus]
|
||||
disable = false
|
||||
@@ -58,7 +58,7 @@ privacyEnhanced = false
|
||||
|
||||
An example Privacy Config that disables all the relevant services in Hugo. With this configuration, the other settings will not matter.
|
||||
|
||||
{{< code-toggle file="config">}}
|
||||
{{< code-toggle file="hugo" >}}
|
||||
[privacy]
|
||||
[privacy.disqus]
|
||||
disable = true
|
||||
@@ -98,7 +98,7 @@ simple
|
||||
|
||||
**Note:** If you use the _simple mode_ for Instagram and a site styled with Bootstrap 4, you may want to disable the inline styles provided by Hugo:
|
||||
|
||||
{{< code-toggle file="config">}}
|
||||
{{< code-toggle file="hugo" >}}
|
||||
[services]
|
||||
[services.instagram]
|
||||
disableInlineCSS = true
|
||||
@@ -114,7 +114,7 @@ simple
|
||||
|
||||
**Note:** If you use the _simple mode_ for Twitter, you may want to disable the inline styles provided by Hugo:
|
||||
|
||||
{{< code-toggle file="config">}}
|
||||
{{< code-toggle file="hugo" >}}
|
||||
[services]
|
||||
[services.twitter]
|
||||
disableInlineCSS = true
|
||||
|
||||
@@ -27,7 +27,7 @@ hugo [flags]
|
||||
-D, --buildDrafts include content marked as draft
|
||||
-E, --buildExpired include expired content
|
||||
-F, --buildFuture include content with publishdate in the future
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
--clock string set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00
|
||||
--config string config file (default is hugo.yaml|json|toml)
|
||||
@@ -44,8 +44,7 @@ hugo [flags]
|
||||
--ignoreCache ignores the cache directory
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--minify minify any supported output format (HTML, XML etc.)
|
||||
--noBuildLock don't create .hugo_build.lock file
|
||||
--noChmod don't sync permission mode of files
|
||||
@@ -65,7 +64,6 @@ hugo [flags]
|
||||
--themesDir string filesystem path to themes directory
|
||||
--trace file write trace to file (not useful in general)
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
-w, --watch watch filesystem for changes and recreate as needed
|
||||
```
|
||||
|
||||
|
||||
@@ -29,13 +29,11 @@ See each sub-command's help for details on how to use the generated script.
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -52,13 +52,11 @@ hugo completion bash
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -43,13 +43,11 @@ hugo completion fish [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -40,13 +40,11 @@ hugo completion powershell [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -54,13 +54,11 @@ hugo completion zsh [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -18,7 +18,13 @@ hugo config [command] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for config
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--format string preferred file format (toml, yaml or json) (default "toml")
|
||||
-h, --help help for config
|
||||
--lang string the language to display config for. Defaults to the first language defined.
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -31,13 +37,11 @@ hugo config [command] [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -14,7 +14,11 @@ hugo config mounts [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for mounts
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for mounts
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -27,13 +31,11 @@ hugo config mounts [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -35,13 +35,11 @@ hugo convert [command] [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -32,15 +32,13 @@ hugo convert toJSON [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
-o, --output string filesystem path to write files to
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
--unsafe enable less safe operations, please backup first
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -32,15 +32,13 @@ hugo convert toTOML [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
-o, --output string filesystem path to write files to
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
--unsafe enable less safe operations, please backup first
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -32,15 +32,13 @@ hugo convert toYAML [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
-o, --output string filesystem path to write files to
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
--unsafe enable less safe operations, please backup first
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -42,13 +42,11 @@ hugo deploy [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -31,13 +31,11 @@ hugo env [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -27,13 +27,11 @@ hugo gen [command] [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -36,13 +36,11 @@ hugo gen chromastyles [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -37,13 +37,11 @@ hugo gen doc [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -34,13 +34,11 @@ hugo gen man [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -33,13 +33,11 @@ hugo import [command] [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -34,13 +34,11 @@ hugo import jekyll [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -33,13 +33,11 @@ hugo list [command] [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -31,13 +31,11 @@ hugo list all [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -31,13 +31,11 @@ hugo list drafts [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -31,13 +31,11 @@ hugo list expired [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -31,13 +31,11 @@ hugo list future [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -38,13 +38,11 @@ See https://gohugo.io/hugo-modules/ for more information.
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -18,9 +18,13 @@ hugo mod clean [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
--all clean entire module cache
|
||||
-h, --help help for clean
|
||||
--pattern string pattern matching module paths to clean (all if not set), e.g. "**hugo*"
|
||||
--all clean entire module cache
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for clean
|
||||
--pattern string pattern matching module paths to clean (all if not set), e.g. "**hugo*"
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -33,13 +37,11 @@ hugo mod clean [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -57,13 +57,11 @@ hugo mod get [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -20,8 +20,12 @@ hugo mod graph [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
--clean delete module cache for dependencies that fail verification
|
||||
-h, --help help for graph
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
--clean delete module cache for dependencies that fail verification
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for graph
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -34,13 +38,11 @@ hugo mod graph [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -25,7 +25,11 @@ hugo mod init [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for init
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for init
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -38,13 +42,11 @@ hugo mod init [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -31,13 +31,11 @@ hugo mod npm [command] [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -28,7 +28,11 @@ hugo mod npm pack [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for pack
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for pack
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -41,13 +45,11 @@ hugo mod npm pack [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -14,7 +14,11 @@ hugo mod tidy [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for tidy
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for tidy
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -27,13 +31,11 @@ hugo mod tidy [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -20,7 +20,11 @@ hugo mod vendor [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for vendor
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for vendor
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -33,13 +37,11 @@ hugo mod vendor [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -18,8 +18,12 @@ hugo mod verify [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
--clean delete module cache for dependencies that fail verification
|
||||
-h, --help help for verify
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
--clean delete module cache for dependencies that fail verification
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for verify
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -32,13 +36,11 @@ hugo mod verify [flags] [args]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -38,13 +38,11 @@ hugo new [command] [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
@@ -25,10 +25,15 @@ hugo new content [path] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--editor string edit new content with this editor, if provided
|
||||
-f, --force overwrite file if it already exists
|
||||
-h, --help help for content
|
||||
-k, --kind string content type to create
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache_$USER/
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--editor string edit new content with this editor, if provided
|
||||
-f, --force overwrite file if it already exists
|
||||
--format string preferred file format (toml, yaml or json) (default "toml")
|
||||
-h, --help help for content
|
||||
-k, --kind string content type to create
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -41,13 +46,11 @@ hugo new content [path] [flags]
|
||||
-d, --destination string filesystem path to write files to
|
||||
-e, --environment string build environment
|
||||
--ignoreVendorPaths string ignores any _vendor for module paths matching the given Glob pattern
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--logLevel string log level (debug|info|warn|error)
|
||||
--quiet build in quiet mode
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user