mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-30 02:02:38 +00:00
Compare commits
435 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 398996e8b0 | |||
| 7f005f5e98 | |||
| 011e3aaa9e | |||
| 3697448450 | |||
| 2638b04c62 | |||
| cf47f43ff1 | |||
| 3b6bd1210a | |||
| 8388cd90e8 | |||
| bc57f5c36e | |||
| 807c551922 | |||
| fe6a6f2737 | |||
| 2650fa772b | |||
| ef525b15d4 | |||
| f916814697 | |||
| cae07ce84b | |||
| 4f9c109dc5 | |||
| f1a00b2069 | |||
| df4cbbd3bd | |||
| 2e2e34a935 | |||
| 2eed35c826 | |||
| dd692c710a | |||
| ed4f1edbd7 | |||
| 058cc6c2c3 | |||
| 4b82f74848 | |||
| 06976ebb87 | |||
| 555a5612b2 | |||
| 75e54345f9 | |||
| df50c108ba | |||
| e27fd4c1b8 | |||
| b7ca3e1b3a | |||
| 13e64d7276 | |||
| 9b26b5487b | |||
| 3dafe206e3 | |||
| 37d6463479 | |||
| 43d446522a | |||
| 4f72e79120 | |||
| fe6676c775 | |||
| 7a97d3e6bc | |||
| 6667c6d743 | |||
| cfda13b363 | |||
| bb2fe814c2 | |||
| e5d66074ce | |||
| d970327d7b | |||
| 0013bea901 | |||
| 30bc4ed0a0 | |||
| f6f22ad944 | |||
| 400fe96aee | |||
| 3f45e729f4 | |||
| ffaa73dc8a | |||
| f8d8c85428 | |||
| 10dc87bf86 | |||
| 7231869ba8 | |||
| ccd3285484 | |||
| 600047ff1c | |||
| 5f2e1cb896 | |||
| c8ce65046d | |||
| a53f962312 | |||
| daffeec30d | |||
| b8b91f5506 | |||
| f0effac804 | |||
| be3ae3ec92 | |||
| 2cf8fe2ea2 | |||
| 3b103cb7b7 | |||
| 0cc936583b | |||
| c15c7da42a | |||
| bcbe57c6e9 | |||
| e38e881248 | |||
| 0665a3951b | |||
| 1ed8c36336 | |||
| e03eb90a36 | |||
| c915d0d325 | |||
| 47d4edce60 | |||
| ea8ef573c6 | |||
| 293e12355d | |||
| fdf3c3b823 | |||
| 312d2252be | |||
| f627903efa | |||
| a2f0f9d475 | |||
| 1ff54ab3d9 | |||
| 456f5476cf | |||
| 09811d0bee | |||
| d351ac2e69 | |||
| 59f57900a4 | |||
| 59cdad325a | |||
| 6022f219f1 | |||
| 0c8a415483 | |||
| f4675fa0f0 | |||
| ebb56e8bdb | |||
| 6b9934a266 | |||
| c7f057797c | |||
| 08d14113b6 | |||
| 94d6d6780f | |||
| 45c9c45d1d | |||
| fce32c07fb | |||
| d32ff16fd6 | |||
| e6eda2a370 | |||
| 66f688f712 | |||
| 2c934be8ab | |||
| 94d0e79d33 | |||
| b9a503febb | |||
| fff132537b | |||
| 8999de193c | |||
| a81c64e43e | |||
| 7ef5096257 | |||
| 16b691a875 | |||
| aefd9a80a6 | |||
| 57ace857aa | |||
| fdff0d3af4 | |||
| 834617f9f8 | |||
| ef20ec1fba | |||
| 5a0ee2b934 | |||
| 6746826689 | |||
| caf608d566 | |||
| 6711dacded | |||
| de23293486 | |||
| 918177ac25 | |||
| 83a980d290 | |||
| 4a16b5f4b0 | |||
| a95896878f | |||
| 873f8805cb | |||
| 3743875778 | |||
| baa62d0abb | |||
| 78c99463fd | |||
| abc54080ec | |||
| a655e00d70 | |||
| 9d973004f5 | |||
| 2247546017 | |||
| d139a037d9 | |||
| 0a88741fe8 | |||
| d999b4e82b | |||
| 20148345aa | |||
| e5052f4e09 | |||
| c81fbf4625 | |||
| a6f199f7a6 | |||
| 0dd06bdac0 | |||
| 5c5384916e | |||
| 56c61559b2 | |||
| 78f8475a05 | |||
| c09ee78fd2 | |||
| f6ae436c58 | |||
| 2182ecfd34 | |||
| e85833d868 | |||
| 43a5aaa778 | |||
| 1639fd20d8 | |||
| 3a44bf182f | |||
| c2a6741394 | |||
| 06bd013641 | |||
| 755d1ffe7a | |||
| fb3cb05cc3 | |||
| d07882dfb7 | |||
| 7f5356717d | |||
| 789ef8c639 | |||
| 71931b30b1 | |||
| d40116e5f9 | |||
| c362634b7d | |||
| d71120852a | |||
| a6b1eb1e91 | |||
| 27110133ff | |||
| 31faf98ce3 | |||
| f14d773841 | |||
| 3aa838cf2b | |||
| a3a26597cf | |||
| 92d776b0df | |||
| e6dd54943f | |||
| 766085c2dc | |||
| b5d13ca16b | |||
| 6b02f5c0f4 | |||
| 88e447c449 | |||
| b718d743b7 | |||
| 0ba19c57f1 | |||
| 0afa2897a0 | |||
| f219ac09f6 | |||
| 786f72302f | |||
| 0cae1cf828 | |||
| 3b18042ee6 | |||
| e38843f4bf | |||
| 87f0dc14ab | |||
| b7d9ab136d | |||
| 04d4c08dbc | |||
| 016dd4a69a | |||
| c6b599a06d | |||
| 3a6784b162 | |||
| 48d7a641d0 | |||
| 2c5a036def | |||
| 592c5b3072 | |||
| a0c4a3660e | |||
| 062510cf1f | |||
| 4e1d0cd9f1 | |||
| a451c49fde | |||
| ff16c42ed0 | |||
| 166483fe12 | |||
| f01505c910 | |||
| 8278384b96 | |||
| 65e610e161 | |||
| b56d9a1294 | |||
| d6fde8fa13 | |||
| 2bac371544 | |||
| 9b4b97a722 | |||
| 501543d4b6 | |||
| 6ffa882349 | |||
| 1eb8b36b38 | |||
| 00c74ee7ff | |||
| e25aa655f4 | |||
| 12679b4083 | |||
| c39ac5738d | |||
| a3535c8486 | |||
| 9da617912b | |||
| 1ab4658c0d | |||
| 75acff5f20 | |||
| 5c9d5413a4 | |||
| 59ebc83d72 | |||
| b6b37a1f00 | |||
| ebe4d39f17 | |||
| 1b0aeeaaf0 | |||
| a4c513caeb | |||
| d741064beb | |||
| 3eb313fef4 | |||
| b93417aa1d | |||
| fd1f4a7860 | |||
| 4c240800a4 | |||
| 38204c4ab6 | |||
| 2f2bc7ff70 | |||
| 242b297f57 | |||
| f44c9ab9cd | |||
| 9f9695cf7b | |||
| 0831d8ccaf | |||
| 1d4ff024ca | |||
| 521e63ac7c | |||
| 47d38628ec | |||
| 0024dcfe3e | |||
| beec1fc98e | |||
| 306573def0 | |||
| 80c8f3b81a | |||
| 6b6dcb44a0 | |||
| d96f2a460f | |||
| ac53035030 | |||
| b874a1ba7a | |||
| 4108705934 | |||
| 9c1e82085e | |||
| 2b73e89d6d | |||
| febf0aec8b | |||
| a4fad5be6b | |||
| 6084f0433c | |||
| cbb7214b6c | |||
| e6136b36f3 | |||
| 659917a002 | |||
| 9d194ab904 | |||
| a305609e18 | |||
| 179de5f5bc | |||
| 5dc1f95b63 | |||
| 6bc892fc24 | |||
| 5f06dbf779 | |||
| 0850e97984 | |||
| a3b4b10f65 | |||
| e3df6478f0 | |||
| 626afc9825 | |||
| e1027c5846 | |||
| e1a052ecb8 | |||
| bfc3488b8e | |||
| ce84b524f4 | |||
| 3cea2932e1 | |||
| 0efd374805 | |||
| 98293eaa15 | |||
| 2b8d907ab7 | |||
| 43338c3a99 | |||
| dea71670c0 | |||
| a5d0a57e6b | |||
| f465571b33 | |||
| f8212d2000 | |||
| 78e8a744b3 | |||
| c790029e1d | |||
| 554553c09c | |||
| de37455ec7 | |||
| 282f6035e7 | |||
| 360fa12213 | |||
| 02aa320030 | |||
| d2640fbc19 | |||
| 1637d12e37 | |||
| 3a7706b069 | |||
| 2955f93fc6 | |||
| 1f0c4e1fb3 | |||
| 91ab455d84 | |||
| ca1e46efb9 | |||
| fd71fa89bd | |||
| b5a3aa7082 | |||
| 3d5928889a | |||
| dc7bc7b4d2 | |||
| 42ed602580 | |||
| 6a2968fd5c | |||
| 23d5fc82ee | |||
| 8531ec7ca3 | |||
| 9f27091e10 | |||
| 187621ae24 | |||
| 4172a835e5 | |||
| fc97388968 | |||
| d67e843c12 | |||
| 6e33c557b0 | |||
| 128f14efad | |||
| 34ee27a78b | |||
| 0f1fc01ef2 | |||
| f32ccd018f | |||
| ffb609f4ab | |||
| 19d91abc92 | |||
| f5c0a2b4b2 | |||
| 31828026a1 | |||
| c74b0f8f9b | |||
| 80949dc73f | |||
| e02629f81a | |||
| b239595af5 | |||
| ed4a345efe | |||
| 9717ac7dce | |||
| 692ec00872 | |||
| d3dd74fd65 | |||
| 80230f26a3 | |||
| 6464981adb | |||
| 2e6712e281 | |||
| 65deb72dc4 | |||
| dc4226a8b2 | |||
| bf5f10faa9 | |||
| 019bd5576b | |||
| c3115292a7 | |||
| 0c6c98e401 | |||
| 90c7749085 | |||
| 13435a6f60 | |||
| 07b96d16e8 | |||
| ceaff7cafc | |||
| b84389c5e0 | |||
| 8de5324479 | |||
| 20cbc2c785 | |||
| 2174525cec | |||
| 7a6192647a | |||
| d68367cbe7 | |||
| 5241f0baee | |||
| b5b36e3200 | |||
| 4f0665f476 | |||
| fc8c5ad824 | |||
| 171caf28a9 | |||
| 0e77f5d4e7 | |||
| 9e7e322ded | |||
| 06827398f9 | |||
| cfc5096e83 | |||
| ab02594e09 | |||
| c71f201fd9 | |||
| 4ed1228d55 | |||
| a51945ea4b | |||
| 7b9dbef41a | |||
| d2b1030060 | |||
| 0bf0e1972d | |||
| 1f244b802e | |||
| 448081b840 | |||
| 1f1d955b56 | |||
| ffcf26e68c | |||
| 9753cb59f1 | |||
| 3bfe8f4be6 | |||
| 6aa2c38507 | |||
| 6d69dac9da | |||
| 9ad46a2035 | |||
| 4256de3392 | |||
| bed7a0faff | |||
| 35ccf06dae | |||
| 4ddcf52ccc | |||
| 353148c2bc | |||
| 568b4335c2 | |||
| 14705ecead | |||
| 69ee6b41e3 | |||
| 88e3568680 | |||
| 2f17f9378a | |||
| 0a7027e2a8 | |||
| 5f24a2c047 | |||
| 710142016b | |||
| f45b522ebf | |||
| 6789207347 | |||
| 0bbdef986d | |||
| 9bd4236e1b | |||
| 4eedb377b6 | |||
| b2b500f563 | |||
| 2fb9af59c1 | |||
| 00e36a4164 | |||
| bfb7744902 | |||
| ecae2106b9 | |||
| 315a73c9fe | |||
| 46dc006f17 | |||
| d089e9d45a | |||
| 90d0d83097 | |||
| 086ae81a98 | |||
| c2bb62d63e | |||
| 34ad9a4f17 | |||
| 914cc85e22 | |||
| 83bef6955e | |||
| a6816db775 | |||
| b6ededf059 | |||
| 8b03c51eae | |||
| 5a9d1324d8 | |||
| f1269a6d00 | |||
| 669b3d3a99 | |||
| 317d81ec48 | |||
| 655983a22d | |||
| 001a28c2f9 | |||
| 391f59f996 | |||
| 459935b291 | |||
| b5dafb0f23 | |||
| 88cb2697e2 | |||
| 6fb2d30d21 | |||
| 288c396439 | |||
| 44e47478d0 | |||
| c7e59367d8 | |||
| 3aed268379 | |||
| 7ef118392a | |||
| 2e77cc07a2 | |||
| 402f6788ee | |||
| 3decf4a327 | |||
| 24c662ce6b | |||
| a215abf70e | |||
| 474bad34ca | |||
| 85535084de | |||
| 19084eaf74 | |||
| 47e7788b3c | |||
| 51af1d2ead | |||
| 1b9dc52ef5 | |||
| d2ec1a06df | |||
| 4d26ab33dc | |||
| d6a2024e6b | |||
| d6982ac0ac | |||
| 3238e14fdf | |||
| 47c05c47e0 | |||
| e590cc26eb | |||
| 74520d2cfd | |||
| e2b277bba5 | |||
| 236f0c840b | |||
| cd6a261242 | |||
| d681ea55a0 | |||
| a96ad5c211 | |||
| 159bed34c3 | |||
| 5cc944ffd7 | |||
| 89d2cf49ac |
@@ -1,8 +1,9 @@
|
||||
defaults: &defaults
|
||||
working_directory: /go/src/github.com/gohugoio
|
||||
docker:
|
||||
- image: bepsays/ci-goreleaser:0.34.2-11
|
||||
|
||||
- image: bepsays/ci-goreleaser:1.11-2
|
||||
environment:
|
||||
CGO_ENABLED: "0"
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
@@ -12,11 +13,11 @@ jobs:
|
||||
path: hugo
|
||||
- run:
|
||||
command: |
|
||||
go get -d github.com/magefile/mage/...
|
||||
git clone git@github.com:gohugoio/hugoDocs.git
|
||||
cd hugo
|
||||
mage vendor
|
||||
mage check
|
||||
go mod download
|
||||
sleep 5
|
||||
go test -p 1 ./...
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths: .
|
||||
@@ -24,7 +25,7 @@ jobs:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /go/src/github.com/gohugoio
|
||||
at: /root/project
|
||||
- run:
|
||||
command: |
|
||||
cd hugo
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
*.md
|
||||
*.log
|
||||
*.txt
|
||||
.git
|
||||
.github
|
||||
.circleci
|
||||
docs
|
||||
examples
|
||||
@@ -0,0 +1,8 @@
|
||||
# Text files have auto line endings
|
||||
* text=auto
|
||||
|
||||
# Go source files always have LF line endings
|
||||
*.go text eol=lf
|
||||
|
||||
# SVG files should not be modified
|
||||
*.svg -text
|
||||
@@ -0,0 +1,3 @@
|
||||
### 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.
|
||||
@@ -15,5 +15,7 @@ vendor/*/
|
||||
*.debug
|
||||
coverage*.out
|
||||
|
||||
dock.sh
|
||||
|
||||
GoBuilds
|
||||
dist
|
||||
|
||||
+13
-7
@@ -1,11 +1,12 @@
|
||||
language: go
|
||||
sudo: false
|
||||
dist: trusty
|
||||
env:
|
||||
HUGO_BUILD_TAGS="extended"
|
||||
git:
|
||||
depth: false
|
||||
go:
|
||||
- 1.9.5
|
||||
- "1.10.1"
|
||||
- 1.11
|
||||
- tip
|
||||
os:
|
||||
- linux
|
||||
@@ -15,13 +16,18 @@ matrix:
|
||||
- go: tip
|
||||
fast_finish: true
|
||||
install:
|
||||
- mkdir -p $HOME/src
|
||||
- mv $HOME/gopath/src/github.com/gohugoio/hugo $HOME/src
|
||||
- export TRAVIS_BUILD_DIR=$HOME/src/hugo
|
||||
- cd $HOME/src/hugo
|
||||
- go get github.com/magefile/mage
|
||||
- mage -v vendor
|
||||
script:
|
||||
- mage -v hugoRace
|
||||
- mage -v check
|
||||
- ./hugo -s docs/
|
||||
- ./hugo --renderToMemory -s docs/
|
||||
- go mod download
|
||||
- mage -v test
|
||||
- mage -v check
|
||||
- mage -v hugo
|
||||
- ./hugo -s docs/
|
||||
- ./hugo --renderToMemory -s docs/
|
||||
before_install:
|
||||
- gem install asciidoctor
|
||||
- type asciidoctor
|
||||
|
||||
+38
-40
@@ -9,7 +9,7 @@ The Hugo community and maintainers are [very active](https://github.com/gohugoio
|
||||
|
||||
*Note that this repository only contains the actual source code of Hugo. For **only** documentation-related pull requests / issues please refer to the [hugoDocs](https://github.com/gohugoio/hugoDocs) repository.*
|
||||
|
||||
*Pull requests that contain changes on the code base **and** related documentation, e.g. for a new feature, shall remain a single, atomic one.*
|
||||
*Changes to the codebase **and** related documentation, e.g. for a new feature, should still use a single pull request.*
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -32,21 +32,24 @@ 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](https://github.com/gohugoio/hugo/issues) 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`) and your operating system.
|
||||
the GitHub [issue tracker](https://github.com/gohugoio/hugo/issues) 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`) and your operating system.
|
||||
|
||||
## Code Contribution
|
||||
|
||||
Hugo has become a fully featured static site generator, so any new functionality must meet these criterias:
|
||||
Hugo has become a fully featured static site generator, so any new functionality must:
|
||||
|
||||
* It must be useful to many.
|
||||
* It must fit natural into _what Hugo does best._
|
||||
* It must strive not to break existing sites.
|
||||
* It must close ur update an open [Hugo issue](https://github.com/gohugoio/hugo/issues)
|
||||
* If it is of some complexity, the contributor is expected to maintain and support the new future (answer questions on the forum, fix any bugs etc.).
|
||||
* be useful to many.
|
||||
* fit naturally into _what Hugo does best._
|
||||
* strive not to break existing sites.
|
||||
* close or update an open [Hugo issue](https://github.com/gohugoio/hugo/issues)
|
||||
|
||||
So, to avoid doing unneeded work, it is recommended to open up a discussion on the [Hugo Forum](https://discourse.gohugo.io/) to get some acceptance that this is a good idea. Also, if this is a complex feature, create a small design proposal on the [Hugo issue tracker](https://github.com/gohugoio/hugo/issues) before you start coding.
|
||||
If it is of some complexity, the contributor is expected to maintain and support the new future (answer questions on the forum, fix any bugs etc.).
|
||||
|
||||
It is recommended to open up a discussion on the [Hugo Forum](https://discourse.gohugo.io/) to get feedback on your idea before you begin. If you are submitting a complex feature, create a small design proposal on the [Hugo issue tracker](https://github.com/gohugoio/hugo/issues) before you start.
|
||||
|
||||
|
||||
**Bug fixes are, of course, always welcome.**
|
||||
@@ -55,16 +58,12 @@ So, to avoid doing unneeded work, it is recommended to open up a discussion on t
|
||||
|
||||
## Submitting Patches
|
||||
|
||||
The Hugo project welcomes all contributors and contributions regardless of skill or experience level.
|
||||
If you are interested in helping with the project, we will help you with your contribution.
|
||||
Hugo is a very active project with many contributions happening daily.
|
||||
Because we want to create the best possible product for our users and the best contribution experience for our developers,
|
||||
we have a set of guidelines which ensure that all contributions are acceptable.
|
||||
The guidelines are not intended as a filter or barrier to participation.
|
||||
If you are unfamiliar with the contribution process, the Hugo team will help you and teach you how to bring your contribution in accordance with the guidelines.
|
||||
The Hugo project welcomes all contributors and contributions regardless of skill or experience level. If you are interested in helping with the project, we will help you with your contribution.
|
||||
|
||||
### Code Contribution Guidelines
|
||||
|
||||
Because we want to create the best possible product for our users and the best contribution experience for our developers, we have a set of guidelines which ensure that all contributions are acceptable. The guidelines are not intended as a filter or barrier to participation. If you are unfamiliar with the contribution process, the Hugo team will help you and teach you how to bring your contribution in accordance with the guidelines.
|
||||
|
||||
To make the contribution process as seamless as possible, we ask for the following:
|
||||
|
||||
* Go ahead and fork the project and make your changes. We encourage pull requests to allow for review and discussion of code changes.
|
||||
@@ -86,11 +85,11 @@ the most important part being that each commit message should have a title/subje
|
||||
Also, if your commit references one or more GitHub issues, always end your commit message body with *See #1234* or *Fixes #1234*.
|
||||
Replace *1234* with the GitHub issue ID. The last example will close the issue when the commit is merged into *master*.
|
||||
|
||||
Sometimes it makes sense to prefix the commit message with the packagename (or docs folder) all lowercased ending with a colon.
|
||||
Sometimes it makes sense to prefix the commit message with the package name (or docs folder) all lowercased ending with a colon.
|
||||
That is fine, but the rest of the rules above apply.
|
||||
So it is "tpl: Add emojify template func", not "tpl: add emojify template func.", and "docs: Document emoji", not "doc: document emoji."
|
||||
|
||||
Please consider to use a short and descriptive branch name, e.g. **NOT** "patch-1". It's very common but creates a naming conflict each time when a submission is pulled for a review.
|
||||
Please use a short and descriptive branch name, e.g. **NOT** "patch-1". It's very common but creates a naming conflict each time when a submission is pulled for a review.
|
||||
|
||||
An example:
|
||||
|
||||
@@ -107,30 +106,23 @@ Fixes #1949
|
||||
|
||||
### Fetching the Sources From GitHub
|
||||
|
||||
Due to the way Go handles package imports, the best approach for working on a
|
||||
Hugo fork is to use Git Remotes. Here's a simple walk-through for getting
|
||||
started:
|
||||
Since Hugo 0.48, Hugo uses the Go Modules support built into Go 1.11 to build. The easiest is is to clone Hugo in a directory outside of `GOPATH`, as in the following example:
|
||||
|
||||
1. Get the Hugo source:
|
||||
```bash
|
||||
mkdir $HOME/src
|
||||
cd $HOME/src
|
||||
git clone https://github.com/gohugoio/hugo.git
|
||||
cd hugo
|
||||
go install
|
||||
```
|
||||
|
||||
```bash
|
||||
go get -u -v -d github.com/gohugoio/hugo
|
||||
```
|
||||
For some convenient build and test targets, you also will want to install Mage:
|
||||
|
||||
1. Install Mage:
|
||||
```bash
|
||||
go get github.com/magefile/mage
|
||||
```
|
||||
|
||||
```bash
|
||||
go get github.com/magefile/mage
|
||||
```
|
||||
|
||||
1. Change to the Hugo source directory and fetch the dependencies:
|
||||
|
||||
```bash
|
||||
cd $HOME/go/src/github.com/gohugoio/hugo
|
||||
mage vendor
|
||||
```
|
||||
|
||||
Note that Hugo uses [Go Dep](https://github.com/golang/dep) to vendor dependencies, rather than a a simple `go get`. We don't commit the vendored packages themselves to the Hugo git repository. The call to `mage vendor` takes care of all this for you.
|
||||
Now, to make a change to Hugos's source:
|
||||
|
||||
1. Create a new branch for your changes (the branch name is arbitrary):
|
||||
|
||||
@@ -193,6 +185,12 @@ To list all available commands along with descriptions:
|
||||
mage -l
|
||||
```
|
||||
|
||||
**Note:** From Hugo 0.43 we have added a build tag, `extended` that adds **SCSS support**. This needs a C compiler installed to build. You can enable this when building by:
|
||||
|
||||
```bash
|
||||
HUGO_BUILD_TAGS=extended mage install
|
||||
````
|
||||
|
||||
### Updating the Hugo Sources
|
||||
|
||||
If you want to stay in sync with the Hugo repository, you can easily pull down
|
||||
|
||||
Regular → Executable
+17
-14
@@ -1,23 +1,26 @@
|
||||
FROM golang:1.9.0-alpine3.6 AS build
|
||||
# GitHub: https://github.com/gohugoio
|
||||
# Twitter: https://twitter.com/gohugoio
|
||||
# Website: https://gohugo.io/
|
||||
|
||||
RUN apk add --no-cache --virtual git musl-dev
|
||||
RUN go get github.com/golang/dep/cmd/dep
|
||||
FROM golang:1.11-alpine3.7 AS build
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
ENV GOOS=linux
|
||||
ENV GO111MODULE=on
|
||||
|
||||
WORKDIR /go/src/github.com/gohugoio/hugo
|
||||
ADD . /go/src/github.com/gohugoio/hugo/
|
||||
RUN dep ensure
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
musl-dev
|
||||
COPY . /go/src/github.com/gohugoio/hugo/
|
||||
RUN go install -ldflags '-s -w'
|
||||
|
||||
FROM alpine:3.6
|
||||
RUN \
|
||||
adduser -h /site -s /sbin/nologin -u 1000 -D hugo && \
|
||||
apk add --no-cache \
|
||||
dumb-init
|
||||
COPY --from=build /go/bin/hugo /bin/hugo
|
||||
USER hugo
|
||||
# ---
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /go/bin/hugo /hugo
|
||||
WORKDIR /site
|
||||
VOLUME /site
|
||||
EXPOSE 1313
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--", "hugo"]
|
||||
ENTRYPOINT [ "/hugo" ]
|
||||
CMD [ "--help" ]
|
||||
|
||||
Generated
-437
@@ -1,437 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/BurntSushi/toml"
|
||||
packages = ["."]
|
||||
revision = "a368813c5e648fee92e5f6c30e3944ff9d5e8895"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/PuerkitoBio/purell"
|
||||
packages = ["."]
|
||||
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/PuerkitoBio/urlesc"
|
||||
packages = ["."]
|
||||
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/alecthomas/chroma"
|
||||
packages = [
|
||||
".",
|
||||
"formatters",
|
||||
"formatters/html",
|
||||
"lexers",
|
||||
"lexers/a",
|
||||
"lexers/b",
|
||||
"lexers/c",
|
||||
"lexers/circular",
|
||||
"lexers/d",
|
||||
"lexers/e",
|
||||
"lexers/f",
|
||||
"lexers/g",
|
||||
"lexers/h",
|
||||
"lexers/i",
|
||||
"lexers/internal",
|
||||
"lexers/j",
|
||||
"lexers/k",
|
||||
"lexers/l",
|
||||
"lexers/m",
|
||||
"lexers/n",
|
||||
"lexers/o",
|
||||
"lexers/p",
|
||||
"lexers/q",
|
||||
"lexers/r",
|
||||
"lexers/s",
|
||||
"lexers/t",
|
||||
"lexers/v",
|
||||
"lexers/w",
|
||||
"lexers/x",
|
||||
"lexers/y",
|
||||
"styles"
|
||||
]
|
||||
revision = "6b1131c0069211a7729895c26f8c51765a4baf25"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/bep/debounce"
|
||||
packages = ["."]
|
||||
revision = "844797fa1dd9ba969d71b62797ff19d1e49d4eac"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/bep/gitmap"
|
||||
packages = ["."]
|
||||
revision = "012701e8669671499fc43e9792335a1dcbfe2afb"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/chaseadamsio/goorgeous"
|
||||
packages = ["."]
|
||||
revision = "dcf1ef873b8987bf12596fe6951c48347986eb2f"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cpuguy83/go-md2man"
|
||||
packages = ["md2man"]
|
||||
revision = "20f5889cbdc3c73dbd2862796665e7c465ade7d1"
|
||||
version = "v1.0.8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/danwakefield/fnmatch"
|
||||
packages = ["."]
|
||||
revision = "cbb64ac3d964b81592e64f957ad53df015803288"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/disintegration/imaging"
|
||||
packages = ["."]
|
||||
revision = "dd50a3ee9985ccd313a2f03c398fcaedc96dc707"
|
||||
version = "v1.2.4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/dlclark/regexp2"
|
||||
packages = [
|
||||
".",
|
||||
"syntax"
|
||||
]
|
||||
revision = "487489b64fb796de2e55f4e8a4ad1e145f80e957"
|
||||
version = "v1.1.6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/eknkc/amber"
|
||||
packages = [
|
||||
".",
|
||||
"parser"
|
||||
]
|
||||
revision = "cdade1c073850f4ffc70a829e31235ea6892853b"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fortytw2/leaktest"
|
||||
packages = ["."]
|
||||
revision = "a5ef70473c97b71626b9abeda80ee92ba2a7de9e"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
packages = ["."]
|
||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||
version = "v1.4.7"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gobwas/glob"
|
||||
packages = [
|
||||
".",
|
||||
"compiler",
|
||||
"match",
|
||||
"syntax",
|
||||
"syntax/ast",
|
||||
"syntax/lexer",
|
||||
"util/runes",
|
||||
"util/strings"
|
||||
]
|
||||
revision = "5ccd90ef52e1e632236f7326478d4faa74f99438"
|
||||
version = "v0.2.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
packages = ["."]
|
||||
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-immutable-radix"
|
||||
packages = ["."]
|
||||
revision = "7f3cd4390caab3250a57f30efdb2a65dd7649ecf"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/golang-lru"
|
||||
packages = ["simplelru"]
|
||||
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
"hcl/ast",
|
||||
"hcl/parser",
|
||||
"hcl/printer",
|
||||
"hcl/scanner",
|
||||
"hcl/strconv",
|
||||
"hcl/token",
|
||||
"json/parser",
|
||||
"json/scanner",
|
||||
"json/token"
|
||||
]
|
||||
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jdkato/prose"
|
||||
packages = [
|
||||
"internal/util",
|
||||
"transform"
|
||||
]
|
||||
revision = "20d3663d4bc9dd10d75abcde9d92e04b4861c674"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kyokomi/emoji"
|
||||
packages = ["."]
|
||||
revision = "7e06b236c489543f53868841f188a294e3383eab"
|
||||
version = "v1.5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/magefile/mage"
|
||||
packages = [
|
||||
"mg",
|
||||
"sh"
|
||||
]
|
||||
revision = "2f974307b636f59c13b88704cf350a4772fef271"
|
||||
version = "v1.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/magiconair/properties"
|
||||
packages = ["."]
|
||||
revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
|
||||
version = "v1.7.6"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/markbates/inflect"
|
||||
packages = ["."]
|
||||
revision = "a12c3aec81a6a938bf584a4bac567afed9256586"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-runewidth"
|
||||
packages = ["."]
|
||||
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/miekg/mmark"
|
||||
packages = ["."]
|
||||
revision = "fd2f6c1403b37925bd7fe13af05853b8ae58ee5f"
|
||||
version = "v1.3.6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/muesli/smartcrop"
|
||||
packages = [
|
||||
".",
|
||||
"options"
|
||||
]
|
||||
revision = "f6ebaa786a12a0fdb2d7c6dee72808e68c296464"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nicksnyder/go-i18n"
|
||||
packages = [
|
||||
"i18n/bundle",
|
||||
"i18n/language",
|
||||
"i18n/translation"
|
||||
]
|
||||
revision = "0dc1626d56435e9d605a29875701721c54bc9bbd"
|
||||
version = "v1.10.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/olekukonko/tablewriter"
|
||||
packages = ["."]
|
||||
revision = "b8a9be070da40449e501c3c4730a889e42d87a9e"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pelletier/go-toml"
|
||||
packages = ["."]
|
||||
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
packages = ["."]
|
||||
revision = "55d61fa8aa702f59229e6cff85793c22e580eaf5"
|
||||
version = "v1.5.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sanity-io/litter"
|
||||
packages = ["."]
|
||||
revision = "ae543b7ba8fd6af63e4976198f146e1348ae53c1"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/shurcooL/sanitized_anchor_name"
|
||||
packages = ["."]
|
||||
revision = "86672fcb3f950f35f2e675df2240550f2a50762f"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/afero"
|
||||
packages = [
|
||||
".",
|
||||
"mem"
|
||||
]
|
||||
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cast"
|
||||
packages = ["."]
|
||||
revision = "8965335b8c7107321228e3e3702cab9832751bac"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = [
|
||||
".",
|
||||
"doc"
|
||||
]
|
||||
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/fsync"
|
||||
packages = ["."]
|
||||
revision = "12a01e648f05a938100a26858d2d59a120307a18"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
packages = ["."]
|
||||
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/nitro"
|
||||
packages = ["."]
|
||||
revision = "24d7ef30a12da0bdc5e2eb370a79c659ddccf0e8"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/viper"
|
||||
packages = ["."]
|
||||
revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
|
||||
version = "v1.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"require"
|
||||
]
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/yosssi/ace"
|
||||
packages = ["."]
|
||||
revision = "ea038f4770b6746c3f8f84f14fa60d9fe1205b56"
|
||||
version = "v0.0.5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/image"
|
||||
packages = [
|
||||
"bmp",
|
||||
"draw",
|
||||
"math/f64",
|
||||
"riff",
|
||||
"tiff",
|
||||
"tiff/lzw",
|
||||
"vp8",
|
||||
"vp8l",
|
||||
"webp"
|
||||
]
|
||||
revision = "f315e440302883054d0c2bd85486878cb4f8572c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"idna"
|
||||
]
|
||||
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["errgroup"]
|
||||
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
"collate/build",
|
||||
"internal/colltab",
|
||||
"internal/gen",
|
||||
"internal/language",
|
||||
"internal/language/compact",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"language",
|
||||
"secure/bidirule",
|
||||
"transform",
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable",
|
||||
"width"
|
||||
]
|
||||
revision = "2cb43934f0eece38629746959acc633cba083fe4"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||
version = "v2.2.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "b9b38ece73fd6c0d0b20eb2ff85dfcc15a82841223b4403566712fd7b7ae7d59"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/BurntSushi/toml"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/PuerkitoBio/purell"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/alecthomas/chroma"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/bep/gitmap"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/chaseadamsio/goorgeous"
|
||||
version = "^1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/disintegration/imaging"
|
||||
version = "~v1.2.4"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/magefile/mage"
|
||||
version = "v1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/eknkc/amber"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fortytw2/leaktest"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
version = "^1.4.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
version = "1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-immutable-radix"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/jdkato/prose"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/kyokomi/emoji"
|
||||
version = "1.5.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/markbates/inflect"
|
||||
revision = "a12c3aec81a6a938bf584a4bac567afed9256586"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/miekg/mmark"
|
||||
version = "^1.3.6"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/nicksnyder/go-i18n"
|
||||
version = "^1.10.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/afero"
|
||||
version = "^1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/cast"
|
||||
version = "^1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
version = "^0.0.1"
|
||||
name = "github.com/spf13/cobra"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/fsync"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/nitro"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/pflag"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/viper"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.1.4"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/olekukonko/tablewriter"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/yosssi/ace"
|
||||
version = "^0.0.5"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/image"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
|
||||
[[constraint]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gobwas/glob"
|
||||
version = "0.2.2"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/muesli/smartcrop"
|
||||
branch = "master"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/sanity-io/litter"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/bep/debounce"
|
||||
version = "^1.1.0"
|
||||
@@ -8,7 +8,7 @@ A Fast and Flexible Static Site Generator built with love by [bep](https://githu
|
||||
[Documentation](https://gohugo.io/overview/introduction/) |
|
||||
[Installation Guide](https://gohugo.io/overview/installing/) |
|
||||
[Contribution Guide](CONTRIBUTING.md) |
|
||||
[Twitter](http://twitter.com/gohugoio)
|
||||
[Twitter](https://twitter.com/gohugoio)
|
||||
|
||||
[](https://godoc.org/github.com/gohugoio/hugo)
|
||||
[](https://travis-ci.org/gohugoio/hugo)
|
||||
@@ -56,23 +56,19 @@ Use the [installation instructions in the Hugo documentation](https://gohugo.io/
|
||||
|
||||
#### Prerequisite Tools
|
||||
|
||||
* [Git](http://git-scm.com/)
|
||||
* [Go (latest or previous version)](https://golang.org/dl/)
|
||||
|
||||
#### Vendored Dependencies
|
||||
|
||||
Hugo uses [dep](https://github.com/golang/dep) to vendor dependencies, but we don't commit the vendored packages themselves to the Hugo git repository. Therefore, a simple `go get` is _not_ supported because the command is not vendor aware.
|
||||
|
||||
The simplest way is to use [mage](https://github.com/magefile/mage) (a Make alternative for Go projects.)
|
||||
* [Git](https://git-scm.com/)
|
||||
* [Go (at least Go 1.11)](https://golang.org/dl/)
|
||||
|
||||
#### Fetch from GitHub
|
||||
|
||||
Since Hugo 0.48, Hugo uses the Go Modules support built into Go 1.11 to build. The easiest is is to clone Hugo in a directory outside of `GOPATH`, as in the following example:
|
||||
|
||||
```bash
|
||||
go get github.com/magefile/mage
|
||||
go get -d github.com/gohugoio/hugo
|
||||
cd ${GOPATH:-$HOME/go}/src/github.com/gohugoio/hugo
|
||||
mage vendor
|
||||
mage install
|
||||
mkdir $HOME/src
|
||||
cd $HOME/src
|
||||
git clone https://github.com/gohugoio/hugo.git
|
||||
cd hugo
|
||||
go install
|
||||
```
|
||||
|
||||
**If you are a Windows user, substitute the `$HOME` environment variable above with `%USERPROFILE%`.**
|
||||
|
||||
+10
-4
@@ -1,18 +1,24 @@
|
||||
image: Visual Studio 2015
|
||||
|
||||
init:
|
||||
- set PATH=%PATH%;C:\MinGW\bin;%GOPATH%\bin
|
||||
- set PATH=%PATH%;C:\mingw-w64\x86_64-7.3.0-posix-seh-rt_v5-rev0\mingw64\bin;%GOPATH%\bin
|
||||
- go version
|
||||
- go env
|
||||
|
||||
environment:
|
||||
HUGO_BUILD_TAGS: extended
|
||||
|
||||
# clones and cd's to path
|
||||
clone_folder: C:\GOPATH\src\github.com\gohugoio\hugo
|
||||
clone_folder: C:\hugo
|
||||
|
||||
install:
|
||||
- gem install asciidoctor
|
||||
# - gem install asciidoctor
|
||||
- pip install docutils
|
||||
- go get github.com/magefile/mage
|
||||
|
||||
build_script:
|
||||
- mage vendor hugoRace
|
||||
- go mod download
|
||||
- mage hugoRace
|
||||
- mage -v check
|
||||
- hugo -s docs/
|
||||
- hugo --renderToMemory -s docs/
|
||||
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
# Temp script used to test new builds.
|
||||
docker run --rm --mount type=bind,source="$(pwd)",target=/go/src/github.com/gohugoio/hugo -w /go/src/github.com/gohugoio/hugo -i -t bepsays/ci-goreleaser:latest /bin/bash
|
||||
Vendored
+30
-11
@@ -24,34 +24,52 @@ type Partition struct {
|
||||
Load func() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
type lazyPartition struct {
|
||||
// Lazy represents a lazily loaded cache.
|
||||
type Lazy struct {
|
||||
initSync sync.Once
|
||||
initErr error
|
||||
cache map[string]interface{}
|
||||
load func() (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
func (l *lazyPartition) init() error {
|
||||
var err error
|
||||
// NewLazy creates a lazy cache with the given load func.
|
||||
func NewLazy(load func() (map[string]interface{}, error)) *Lazy {
|
||||
return &Lazy{load: load}
|
||||
}
|
||||
|
||||
func (l *Lazy) init() error {
|
||||
l.initSync.Do(func() {
|
||||
var c map[string]interface{}
|
||||
c, err = l.load()
|
||||
c, err := l.load()
|
||||
l.cache = c
|
||||
l.initErr = err
|
||||
|
||||
})
|
||||
|
||||
return err
|
||||
return l.initErr
|
||||
}
|
||||
|
||||
// Get initializes the cache if not already initialized, then looks up the
|
||||
// given key.
|
||||
func (l *Lazy) Get(key string) (interface{}, bool, error) {
|
||||
l.init()
|
||||
if l.initErr != nil {
|
||||
return nil, false, l.initErr
|
||||
}
|
||||
v, found := l.cache[key]
|
||||
return v, found, nil
|
||||
}
|
||||
|
||||
// PartitionedLazyCache is a lazily loaded cache paritioned by a supplied string key.
|
||||
type PartitionedLazyCache struct {
|
||||
partitions map[string]*lazyPartition
|
||||
partitions map[string]*Lazy
|
||||
}
|
||||
|
||||
// NewPartitionedLazyCache creates a new NewPartitionedLazyCache with the supplied
|
||||
// partitions.
|
||||
func NewPartitionedLazyCache(partitions ...Partition) *PartitionedLazyCache {
|
||||
lazyPartitions := make(map[string]*lazyPartition, len(partitions))
|
||||
lazyPartitions := make(map[string]*Lazy, len(partitions))
|
||||
for _, partition := range partitions {
|
||||
lazyPartitions[partition.Key] = &lazyPartition{load: partition.Load}
|
||||
lazyPartitions[partition.Key] = NewLazy(partition.Load)
|
||||
}
|
||||
cache := &PartitionedLazyCache{partitions: lazyPartitions}
|
||||
|
||||
@@ -67,11 +85,12 @@ func (c *PartitionedLazyCache) Get(partition, key string) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err := p.init(); err != nil {
|
||||
v, found, err := p.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, found := p.cache[key]; found {
|
||||
if found {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ func (c *benchmarkCmd) benchmark(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
comm, err := initializeConfig(false, &c.hugoBuilderCommon, c, cfgInit)
|
||||
comm, err := initializeConfig(true, false, &c.hugoBuilderCommon, c, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+137
-49
@@ -16,6 +16,8 @@ package commands
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -23,8 +25,6 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/gohugoio/hugo/utils"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
@@ -34,38 +34,44 @@ import (
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
src "github.com/gohugoio/hugo/source"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
)
|
||||
|
||||
type commandeer struct {
|
||||
type commandeerHugoState struct {
|
||||
*deps.DepsCfg
|
||||
hugo *hugolib.HugoSites
|
||||
fsCreate sync.Once
|
||||
}
|
||||
|
||||
hugo *hugolib.HugoSites
|
||||
type commandeer struct {
|
||||
*commandeerHugoState
|
||||
|
||||
// Currently only set when in "fast render mode". But it seems to
|
||||
// be fast enough that we could maybe just add it for all server modes.
|
||||
changeDetector *fileChangeDetector
|
||||
|
||||
// We need to reuse this on server rebuilds.
|
||||
destinationFs afero.Fs
|
||||
|
||||
h *hugoBuilderCommon
|
||||
ftch flagsToConfigHandler
|
||||
|
||||
pathSpec *helpers.PathSpec
|
||||
visitedURLs *types.EvictingStringQueue
|
||||
|
||||
staticDirsConfig []*src.Dirs
|
||||
|
||||
// We watch these for changes.
|
||||
configFiles []string
|
||||
|
||||
doWithCommandeer func(c *commandeer) error
|
||||
|
||||
// We can do this only once.
|
||||
fsCreate sync.Once
|
||||
// We watch these for changes.
|
||||
configFiles []string
|
||||
|
||||
// Used in cases where we get flooded with events in server mode.
|
||||
debounce func(f func())
|
||||
|
||||
serverPorts []int
|
||||
languagesConfigured bool
|
||||
languages helpers.Languages
|
||||
languages langs.Languages
|
||||
|
||||
configured bool
|
||||
paused bool
|
||||
}
|
||||
|
||||
func (c *commandeer) Set(key string, value interface{}) {
|
||||
@@ -75,31 +81,14 @@ func (c *commandeer) Set(key string, value interface{}) {
|
||||
c.Cfg.Set(key, value)
|
||||
}
|
||||
|
||||
// PathSpec lazily creates a new PathSpec, as all the paths must
|
||||
// be configured before it is created.
|
||||
func (c *commandeer) PathSpec() *helpers.PathSpec {
|
||||
c.configured = true
|
||||
return c.pathSpec
|
||||
}
|
||||
|
||||
func (c *commandeer) initFs(fs *hugofs.Fs) error {
|
||||
c.destinationFs = fs.Destination
|
||||
c.DepsCfg.Fs = fs
|
||||
ps, err := helpers.NewPathSpec(fs, c.Cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.pathSpec = ps
|
||||
|
||||
dirsConfig, err := c.createStaticDirsConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.staticDirsConfig = dirsConfig
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newCommandeer(running bool, h *hugoBuilderCommon, f flagsToConfigHandler, doWithCommandeer func(c *commandeer) error, subCmdVs ...*cobra.Command) (*commandeer, error) {
|
||||
func newCommandeer(mustHaveConfigFile, running bool, h *hugoBuilderCommon, f flagsToConfigHandler, doWithCommandeer func(c *commandeer) error, subCmdVs ...*cobra.Command) (*commandeer, error) {
|
||||
|
||||
var rebuildDebouncer func(f func())
|
||||
if running {
|
||||
@@ -110,17 +99,80 @@ func newCommandeer(running bool, h *hugoBuilderCommon, f flagsToConfigHandler, d
|
||||
}
|
||||
|
||||
c := &commandeer{
|
||||
h: h,
|
||||
ftch: f,
|
||||
doWithCommandeer: doWithCommandeer,
|
||||
visitedURLs: types.NewEvictingStringQueue(10),
|
||||
debounce: rebuildDebouncer,
|
||||
h: h,
|
||||
ftch: f,
|
||||
commandeerHugoState: &commandeerHugoState{},
|
||||
doWithCommandeer: doWithCommandeer,
|
||||
visitedURLs: types.NewEvictingStringQueue(10),
|
||||
debounce: rebuildDebouncer,
|
||||
}
|
||||
|
||||
return c, c.loadConfig(running)
|
||||
return c, c.loadConfig(mustHaveConfigFile, running)
|
||||
}
|
||||
|
||||
func (c *commandeer) loadConfig(running bool) error {
|
||||
type fileChangeDetector struct {
|
||||
sync.Mutex
|
||||
current map[string]string
|
||||
prev map[string]string
|
||||
|
||||
irrelevantRe *regexp.Regexp
|
||||
}
|
||||
|
||||
func (f *fileChangeDetector) OnFileClose(name, md5sum string) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
f.current[name] = md5sum
|
||||
}
|
||||
|
||||
func (f *fileChangeDetector) changed() []string {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
var c []string
|
||||
for k, v := range f.current {
|
||||
vv, found := f.prev[k]
|
||||
if !found || v != vv {
|
||||
c = append(c, k)
|
||||
}
|
||||
}
|
||||
|
||||
return f.filterIrrelevant(c)
|
||||
}
|
||||
|
||||
func (f *fileChangeDetector) filterIrrelevant(in []string) []string {
|
||||
var filtered []string
|
||||
for _, v := range in {
|
||||
if !f.irrelevantRe.MatchString(v) {
|
||||
filtered = append(filtered, v)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func (f *fileChangeDetector) PrepareNew() {
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
if f.current == nil {
|
||||
f.current = make(map[string]string)
|
||||
f.prev = make(map[string]string)
|
||||
return
|
||||
}
|
||||
|
||||
f.prev = make(map[string]string)
|
||||
for k, v := range f.current {
|
||||
f.prev[k] = v
|
||||
}
|
||||
f.current = make(map[string]string)
|
||||
}
|
||||
|
||||
func (c *commandeer) loadConfig(mustHaveConfigFile, running bool) error {
|
||||
|
||||
if c.DepsCfg == nil {
|
||||
c.DepsCfg = &deps.DepsCfg{}
|
||||
@@ -168,12 +220,18 @@ func (c *commandeer) loadConfig(running bool) error {
|
||||
doWithConfig)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
if mustHaveConfigFile {
|
||||
return err
|
||||
}
|
||||
if err != hugolib.ErrNoConfigFile {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
c.configFiles = configFiles
|
||||
|
||||
if l, ok := c.Cfg.Get("languagesSorted").(helpers.Languages); ok {
|
||||
if l, ok := c.Cfg.Get("languagesSorted").(langs.Languages); ok {
|
||||
c.languagesConfigured = true
|
||||
c.languages = l
|
||||
}
|
||||
@@ -203,12 +261,41 @@ func (c *commandeer) loadConfig(running bool) error {
|
||||
c.fsCreate.Do(func() {
|
||||
fs := hugofs.NewFrom(sourceFs, config)
|
||||
|
||||
// Hugo writes the output to memory instead of the disk.
|
||||
if createMemFs {
|
||||
if c.destinationFs != nil {
|
||||
// Need to reuse the destination on server rebuilds.
|
||||
fs.Destination = c.destinationFs
|
||||
} else if createMemFs {
|
||||
// Hugo writes the output to memory instead of the disk.
|
||||
fs.Destination = new(afero.MemMapFs)
|
||||
}
|
||||
|
||||
doLiveReload := !c.h.buildWatch && !config.GetBool("disableLiveReload")
|
||||
fastRenderMode := doLiveReload && !config.GetBool("disableFastRender")
|
||||
|
||||
if fastRenderMode {
|
||||
// For now, fast render mode only. It should, however, be fast enough
|
||||
// for the full variant, too.
|
||||
changeDetector := &fileChangeDetector{
|
||||
// We use this detector to decide to do a Hot reload of a single path or not.
|
||||
// We need to filter out source maps and possibly some other to be able
|
||||
// to make that decision.
|
||||
irrelevantRe: regexp.MustCompile(`\.map$`),
|
||||
}
|
||||
changeDetector.PrepareNew()
|
||||
fs.Destination = hugofs.NewHashingFs(fs.Destination, changeDetector)
|
||||
c.changeDetector = changeDetector
|
||||
}
|
||||
|
||||
err = c.initFs(fs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var h *hugolib.HugoSites
|
||||
|
||||
h, err = hugolib.NewHugoSites(*c.DepsCfg)
|
||||
c.hugo = h
|
||||
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -221,7 +308,7 @@ func (c *commandeer) loadConfig(running bool) error {
|
||||
cacheDir = cacheDir + helpers.FilePathSeparator
|
||||
}
|
||||
isDir, err := helpers.DirExists(cacheDir, sourceFs)
|
||||
utils.CheckErr(cfg.Logger, err)
|
||||
checkErr(cfg.Logger, err)
|
||||
if !isDir {
|
||||
mkdir(cacheDir)
|
||||
}
|
||||
@@ -232,18 +319,19 @@ func (c *commandeer) loadConfig(running bool) error {
|
||||
|
||||
cfg.Logger.INFO.Println("Using config file:", config.ConfigFileUsed())
|
||||
|
||||
themeDir := c.PathSpec().GetThemeDir()
|
||||
themeDir := c.hugo.PathSpec.GetFirstThemeDir()
|
||||
if themeDir != "" {
|
||||
if _, err := sourceFs.Stat(themeDir); os.IsNotExist(err) {
|
||||
return newSystemError("Unable to find theme Directory:", themeDir)
|
||||
}
|
||||
}
|
||||
|
||||
themeVersionMismatch, minVersion := c.isThemeVsHugoVersionMismatch(sourceFs)
|
||||
dir, themeVersionMismatch, minVersion := c.isThemeVsHugoVersionMismatch(sourceFs)
|
||||
|
||||
if themeVersionMismatch {
|
||||
cfg.Logger.ERROR.Printf("Current theme does not support Hugo version %s. Minimum version required is %s\n",
|
||||
helpers.CurrentHugoVersion.ReleaseVersion(), minVersion)
|
||||
name := filepath.Base(dir)
|
||||
cfg.Logger.ERROR.Printf("%s theme does not support Hugo version %s. Minimum version required is %s\n",
|
||||
strings.ToUpper(name), helpers.CurrentHugoVersion.ReleaseVersion(), minVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+41
-5
@@ -14,9 +14,12 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/spf13/cobra"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
|
||||
"github.com/spf13/nitro"
|
||||
)
|
||||
@@ -148,7 +151,7 @@ Complete documentation is available at http://gohugo.io/.`,
|
||||
return nil
|
||||
}
|
||||
|
||||
c, err := initializeConfig(cc.buildWatch, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
c, err := initializeConfig(true, cc.buildWatch, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -174,6 +177,7 @@ Complete documentation is available at http://gohugo.io/.`,
|
||||
cc.cmd.Flags().BoolVarP(&cc.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
|
||||
|
||||
cc.cmd.Flags().Bool("renderToMemory", false, "render to memory (only useful for benchmark testing)")
|
||||
cc.cmd.Flags().Bool("minify", false, "minify any supported output format (HTML, XML etc.)")
|
||||
|
||||
// Set bash-completion
|
||||
_ = cc.cmd.PersistentFlags().SetAnnotation("logFile", cobra.BashCompFilenameExt, []string{})
|
||||
@@ -216,8 +220,6 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().StringP("destination", "d", "", "filesystem path to write files to")
|
||||
cmd.Flags().StringP("theme", "t", "", "theme to use (located in /themes/THEMENAME/)")
|
||||
cmd.Flags().StringP("themesDir", "", "", "filesystem path to themes directory")
|
||||
cmd.Flags().Bool("uglyURLs", false, "(deprecated) if true, use /filename.html instead of /filename/")
|
||||
cmd.Flags().Bool("canonifyURLs", false, "(deprecated) if true, all relative URLs will be canonicalized using baseURL")
|
||||
cmd.Flags().StringVarP(&cc.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. http://spf13.com/")
|
||||
cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date and author info to the pages")
|
||||
cmd.Flags().BoolVar(&cc.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build")
|
||||
@@ -225,8 +227,6 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().BoolVar(&nitro.AnalysisOn, "stepAnalysis", false, "display memory and timing of different steps of the program")
|
||||
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().Bool("pluralizeListTitles", true, "(deprecated) pluralize titles in lists using inflect")
|
||||
cmd.Flags().Bool("preserveTaxonomyNames", false, `(deprecated) preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")`)
|
||||
cmd.Flags().BoolP("forceSyncStatic", "", false, "copy all files when static is changed.")
|
||||
cmd.Flags().BoolP("noTimes", "", false, "don't sync modification time of files")
|
||||
cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files")
|
||||
@@ -241,3 +241,39 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
|
||||
_ = cmd.Flags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
|
||||
}
|
||||
|
||||
func checkErr(logger *jww.Notepad, err error, s ...string) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if len(s) == 0 {
|
||||
logger.CRITICAL.Println(err)
|
||||
return
|
||||
}
|
||||
for _, message := range s {
|
||||
logger.ERROR.Println(message)
|
||||
}
|
||||
logger.ERROR.Println(err)
|
||||
}
|
||||
|
||||
func stopOnErr(logger *jww.Notepad, err error, s ...string) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer os.Exit(-1)
|
||||
|
||||
if len(s) == 0 {
|
||||
newMessage := err.Error()
|
||||
// Printing an empty string results in a error with
|
||||
// no message, no bueno.
|
||||
if newMessage != "" {
|
||||
logger.CRITICAL.Println(newMessage)
|
||||
}
|
||||
}
|
||||
for _, message := range s {
|
||||
if message != "" {
|
||||
logger.CRITICAL.Println(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func TestCommandsPersistentFlags(t *testing.T) {
|
||||
"--port=1366",
|
||||
"--renderToDisk",
|
||||
"--source=mysource",
|
||||
"--uglyURLs"}, func(commands []cmder) {
|
||||
}, func(commands []cmder) {
|
||||
var sc *serverCmd
|
||||
for _, command := range commands {
|
||||
if b, ok := command.(commandsBuilderGetter); ok {
|
||||
@@ -105,7 +105,6 @@ func TestCommandsPersistentFlags(t *testing.T) {
|
||||
|
||||
assert.Equal([]string{"page", "home"}, cfg.Get("disableKinds"))
|
||||
|
||||
assert.True(cfg.GetBool("uglyURLs"))
|
||||
assert.True(cfg.GetBool("gc"))
|
||||
|
||||
// The flag is named i18n-warnings
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ func newConfigCmd() *configCmd {
|
||||
}
|
||||
|
||||
func (c *configCmd) printConfig(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := initializeConfig(false, &c.hugoBuilderCommon, c, nil)
|
||||
cfg, err := initializeConfig(true, false, &c.hugoBuilderCommon, c, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ func (cc *convertCmd) convertContents(mark rune) error {
|
||||
return newUserError("Unsafe operation not allowed, use --unsafe or set a different output path")
|
||||
}
|
||||
|
||||
c, err := initializeConfig(false, &cc.hugoBuilderCommon, cc, nil)
|
||||
c, err := initializeConfig(true, false, &cc.hugoBuilderCommon, cc, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,13 +23,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
ansiEsc = "\u001B"
|
||||
clearLine = "\r\033[K"
|
||||
hideCursor = ansiEsc + "[?25l"
|
||||
showCursor = ansiEsc + "[?25h"
|
||||
)
|
||||
|
||||
type flagsToConfigHandler interface {
|
||||
flagsToConfig(cfg config.Provider)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package commands
|
||||
|
||||
const (
|
||||
ansiEsc = "\u001B"
|
||||
clearLine = "\r\033[K"
|
||||
hideCursor = ansiEsc + "[?25l"
|
||||
showCursor = ansiEsc + "[?25h"
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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.
|
||||
|
||||
// +build windows
|
||||
|
||||
package commands
|
||||
|
||||
const (
|
||||
ansiEsc = ""
|
||||
clearLine = ""
|
||||
hideCursor = ""
|
||||
showCursor = ""
|
||||
)
|
||||
+199
-209
@@ -23,6 +23,8 @@ import (
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib/filesystems"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"log"
|
||||
@@ -32,8 +34,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
src "github.com/gohugoio/hugo/source"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
@@ -43,7 +43,6 @@ import (
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/livereload"
|
||||
"github.com/gohugoio/hugo/utils"
|
||||
"github.com/gohugoio/hugo/watcher"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -63,6 +62,8 @@ type Response struct {
|
||||
Cmd *cobra.Command
|
||||
}
|
||||
|
||||
// IsUserError returns true is the Response error is a user error rather than a
|
||||
// system error.
|
||||
func (r Response) IsUserError() bool {
|
||||
return r.Err != nil && isUserError(r.Err)
|
||||
}
|
||||
@@ -84,11 +85,11 @@ func Execute(args []string) Response {
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
errCount := jww.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)
|
||||
errCount := int(jww.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError))
|
||||
if errCount > 0 {
|
||||
err = fmt.Errorf("logged %d errors", errCount)
|
||||
} else if resp.Result != nil {
|
||||
errCount = resp.Result.Log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)
|
||||
errCount = resp.Result.NumLogErrors()
|
||||
if errCount > 0 {
|
||||
err = fmt.Errorf("logged %d errors", errCount)
|
||||
}
|
||||
@@ -103,12 +104,12 @@ func Execute(args []string) Response {
|
||||
}
|
||||
|
||||
// InitializeConfig initializes a config file with sensible default configuration flags.
|
||||
func initializeConfig(running bool,
|
||||
func initializeConfig(mustHaveConfigFile, running bool,
|
||||
h *hugoBuilderCommon,
|
||||
f flagsToConfigHandler,
|
||||
doWithCommandeer func(c *commandeer) error) (*commandeer, error) {
|
||||
|
||||
c, err := newCommandeer(running, h, f, doWithCommandeer)
|
||||
c, err := newCommandeer(mustHaveConfigFile, running, h, f, doWithCommandeer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -210,35 +211,31 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
|
||||
"verboseLog",
|
||||
}
|
||||
|
||||
// Will set a value even if it is the default.
|
||||
flagKeysForced := []string{
|
||||
"minify",
|
||||
}
|
||||
|
||||
for _, key := range persFlagKeys {
|
||||
setValueFromFlag(cmd.PersistentFlags(), key, cfg, "")
|
||||
setValueFromFlag(cmd.PersistentFlags(), key, cfg, "", false)
|
||||
}
|
||||
for _, key := range flagKeys {
|
||||
setValueFromFlag(cmd.Flags(), key, cfg, "")
|
||||
setValueFromFlag(cmd.Flags(), key, cfg, "", false)
|
||||
}
|
||||
|
||||
for _, key := range flagKeysForced {
|
||||
setValueFromFlag(cmd.Flags(), key, cfg, "", true)
|
||||
}
|
||||
|
||||
// Set some "config aliases"
|
||||
setValueFromFlag(cmd.Flags(), "destination", cfg, "publishDir")
|
||||
setValueFromFlag(cmd.Flags(), "i18n-warnings", cfg, "logI18nWarnings")
|
||||
setValueFromFlag(cmd.Flags(), "destination", cfg, "publishDir", false)
|
||||
setValueFromFlag(cmd.Flags(), "i18n-warnings", cfg, "logI18nWarnings", false)
|
||||
|
||||
}
|
||||
|
||||
var deprecatedFlags = map[string]bool{
|
||||
strings.ToLower("uglyURLs"): true,
|
||||
strings.ToLower("pluralizeListTitles"): true,
|
||||
strings.ToLower("preserveTaxonomyNames"): true,
|
||||
strings.ToLower("canonifyURLs"): true,
|
||||
}
|
||||
|
||||
func setValueFromFlag(flags *flag.FlagSet, key string, cfg config.Provider, targetKey string) {
|
||||
func setValueFromFlag(flags *flag.FlagSet, key string, cfg config.Provider, targetKey string, force bool) {
|
||||
key = strings.TrimSpace(key)
|
||||
if flags.Changed(key) {
|
||||
if _, deprecated := deprecatedFlags[strings.ToLower(key)]; deprecated {
|
||||
msg := fmt.Sprintf(`Set "%s = true" in your config.toml.
|
||||
If you need to set this configuration value from the command line, set it via an OS environment variable: "HUGO_%s=true hugo"`, key, strings.ToUpper(key))
|
||||
// Remove in Hugo 0.38
|
||||
helpers.Deprecated("hugo", "--"+key+" flag", msg, true)
|
||||
}
|
||||
if (force && flags.Lookup(key) != nil) || flags.Changed(key) {
|
||||
f := flags.Lookup(key)
|
||||
configKey := key
|
||||
if targetKey != "" {
|
||||
@@ -277,9 +274,13 @@ func (c *commandeer) fullBuild() error {
|
||||
copyStaticFunc := func() error {
|
||||
cnt, err := c.copyStatic()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error copying static files: %s", err)
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("Error copying static files: %s", err)
|
||||
}
|
||||
c.Logger.WARN.Println("No Static directory found")
|
||||
}
|
||||
langCount = cnt
|
||||
langCount = cnt
|
||||
return nil
|
||||
}
|
||||
buildSitesFunc := func() error {
|
||||
@@ -344,10 +345,10 @@ func (c *commandeer) build() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Logger.FEEDBACK.Println("Watching for changes in", c.PathSpec().AbsPathify(c.Cfg.GetString("contentDir")))
|
||||
c.Logger.FEEDBACK.Println("Watching for changes in", c.hugo.PathSpec.AbsPathify(c.Cfg.GetString("contentDir")))
|
||||
c.Logger.FEEDBACK.Println("Press Ctrl+C to stop")
|
||||
watcher, err := c.newWatcher(watchDirs...)
|
||||
utils.CheckErr(c.Logger, err)
|
||||
checkErr(c.Logger, err)
|
||||
defer watcher.Close()
|
||||
|
||||
var sigs = make(chan os.Signal)
|
||||
@@ -380,49 +381,30 @@ func (c *commandeer) copyStatic() (map[string]uint64, error) {
|
||||
return c.doWithPublishDirs(c.copyStaticTo)
|
||||
}
|
||||
|
||||
func (c *commandeer) createStaticDirsConfig() ([]*src.Dirs, error) {
|
||||
var dirsConfig []*src.Dirs
|
||||
|
||||
if !c.languages.IsMultihost() {
|
||||
dirs, err := src.NewDirs(c.Fs, c.Cfg, c.DepsCfg.Logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dirsConfig = append(dirsConfig, dirs)
|
||||
} else {
|
||||
for _, l := range c.languages {
|
||||
dirs, err := src.NewDirs(c.Fs, l, c.DepsCfg.Logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dirsConfig = append(dirsConfig, dirs)
|
||||
}
|
||||
}
|
||||
|
||||
return dirsConfig, nil
|
||||
|
||||
}
|
||||
|
||||
func (c *commandeer) doWithPublishDirs(f func(dirs *src.Dirs, publishDir string) (uint64, error)) (map[string]uint64, error) {
|
||||
func (c *commandeer) doWithPublishDirs(f func(sourceFs *filesystems.SourceFilesystem) (uint64, error)) (map[string]uint64, error) {
|
||||
|
||||
langCount := make(map[string]uint64)
|
||||
|
||||
for _, dirs := range c.staticDirsConfig {
|
||||
staticFilesystems := c.hugo.BaseFs.SourceFilesystems.Static
|
||||
|
||||
cnt, err := f(dirs, c.pathSpec.PublishDir)
|
||||
if len(staticFilesystems) == 0 {
|
||||
c.Logger.WARN.Println("No static directories found to sync")
|
||||
return langCount, nil
|
||||
}
|
||||
|
||||
for lang, fs := range staticFilesystems {
|
||||
cnt, err := f(fs)
|
||||
if err != nil {
|
||||
return langCount, err
|
||||
}
|
||||
|
||||
if dirs.Language == nil {
|
||||
if lang == "" {
|
||||
// Not multihost
|
||||
for _, l := range c.languages {
|
||||
langCount[l.Lang] = cnt
|
||||
}
|
||||
} else {
|
||||
langCount[dirs.Language.Lang] = cnt
|
||||
langCount[lang] = cnt
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return langCount, nil
|
||||
@@ -443,29 +425,18 @@ func (fs *countingStatFs) Stat(name string) (os.FileInfo, error) {
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (c *commandeer) copyStaticTo(dirs *src.Dirs, publishDir string) (uint64, error) {
|
||||
|
||||
func (c *commandeer) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
|
||||
publishDir := c.hugo.PathSpec.PublishDir
|
||||
// If root, remove the second '/'
|
||||
if publishDir == "//" {
|
||||
publishDir = helpers.FilePathSeparator
|
||||
}
|
||||
|
||||
if dirs.Language != nil {
|
||||
// Multihost setup.
|
||||
publishDir = filepath.Join(publishDir, dirs.Language.Lang)
|
||||
if sourceFs.PublishFolder != "" {
|
||||
publishDir = filepath.Join(publishDir, sourceFs.PublishFolder)
|
||||
}
|
||||
|
||||
staticSourceFs, err := dirs.CreateStaticFs()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if staticSourceFs == nil {
|
||||
c.Logger.WARN.Println("No static directories found to sync")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
fs := &countingStatFs{Fs: staticSourceFs}
|
||||
fs := &countingStatFs{Fs: sourceFs.Fs}
|
||||
|
||||
syncer := fsync.NewSyncer()
|
||||
syncer.NoTimes = c.Cfg.GetBool("noTimes")
|
||||
@@ -485,6 +456,8 @@ func (c *commandeer) copyStaticTo(dirs *src.Dirs, publishDir string) (uint64, er
|
||||
}
|
||||
c.Logger.INFO.Println("syncing static files to", publishDir)
|
||||
|
||||
var err error
|
||||
|
||||
// because we are using a baseFs (to get the union right).
|
||||
// set sync src to root
|
||||
err = syncer.Sync(publishDir, helpers.FilePathSeparator)
|
||||
@@ -498,6 +471,10 @@ func (c *commandeer) copyStaticTo(dirs *src.Dirs, publishDir string) (uint64, er
|
||||
return numFiles, err
|
||||
}
|
||||
|
||||
func (c *commandeer) firstPathSpec() *helpers.PathSpec {
|
||||
return c.hugo.Sites[0].PathSpec
|
||||
}
|
||||
|
||||
func (c *commandeer) timeTrack(start time.Time, name string) {
|
||||
if c.h.quiet {
|
||||
return
|
||||
@@ -514,41 +491,10 @@ func (c *commandeer) getDirList() ([]string, error) {
|
||||
var seen = make(map[string]bool)
|
||||
var nested []string
|
||||
|
||||
dataDir := c.PathSpec().AbsPathify(c.Cfg.GetString("dataDir"))
|
||||
i18nDir := c.PathSpec().AbsPathify(c.Cfg.GetString("i18nDir"))
|
||||
staticSyncer, err := newStaticSyncer(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layoutDir := c.PathSpec().GetLayoutDirPath()
|
||||
staticDirs := staticSyncer.d.AbsStaticDirs
|
||||
|
||||
newWalker := func(allowSymbolicDirs bool) func(path string, fi os.FileInfo, err error) error {
|
||||
return func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
if path == dataDir && os.IsNotExist(err) {
|
||||
c.Logger.WARN.Println("Skip dataDir:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if path == i18nDir && os.IsNotExist(err) {
|
||||
c.Logger.WARN.Println("Skip i18nDir:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if path == layoutDir && os.IsNotExist(err) {
|
||||
c.Logger.WARN.Println("Skip layoutDir:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
for _, staticDir := range staticDirs {
|
||||
if path == staticDir && os.IsNotExist(err) {
|
||||
c.Logger.WARN.Println("Skip staticDir:", err)
|
||||
}
|
||||
}
|
||||
// Ignore.
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -605,23 +551,32 @@ func (c *commandeer) getDirList() ([]string, error) {
|
||||
regularWalker := newWalker(false)
|
||||
|
||||
// SymbolicWalk will log anny ERRORs
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, dataDir, regularWalker)
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, i18nDir, regularWalker)
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, layoutDir, regularWalker)
|
||||
|
||||
for _, contentDir := range c.PathSpec().ContentDirs() {
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, contentDir.Value, symLinkWalker)
|
||||
// Also note that the Dirnames fetched below will contain any relevant theme
|
||||
// directories.
|
||||
for _, contentDir := range c.hugo.PathSpec.BaseFs.Content.Dirnames {
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, contentDir, symLinkWalker)
|
||||
}
|
||||
|
||||
for _, staticDir := range staticDirs {
|
||||
for _, staticDir := range c.hugo.PathSpec.BaseFs.Data.Dirnames {
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, staticDir, regularWalker)
|
||||
}
|
||||
|
||||
if c.PathSpec().ThemeSet() {
|
||||
themesDir := c.PathSpec().GetThemeDir()
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "layouts"), regularWalker)
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "i18n"), regularWalker)
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "data"), regularWalker)
|
||||
for _, staticDir := range c.hugo.PathSpec.BaseFs.I18n.Dirnames {
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, staticDir, regularWalker)
|
||||
}
|
||||
|
||||
for _, staticDir := range c.hugo.PathSpec.BaseFs.Layouts.Dirnames {
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, staticDir, regularWalker)
|
||||
}
|
||||
|
||||
for _, staticFilesystem := range c.hugo.PathSpec.BaseFs.Static {
|
||||
for _, staticDir := range staticFilesystem.Dirnames {
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, staticDir, regularWalker)
|
||||
}
|
||||
}
|
||||
|
||||
for _, assetDir := range c.hugo.PathSpec.BaseFs.Assets.Dirnames {
|
||||
_ = helpers.SymbolicWalk(c.Fs.Source, assetDir, regularWalker)
|
||||
}
|
||||
|
||||
if len(nested) > 0 {
|
||||
@@ -646,69 +601,31 @@ func (c *commandeer) getDirList() ([]string, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (c *commandeer) recreateAndBuildSites(watching bool) (err error) {
|
||||
defer c.timeTrack(time.Now(), "Total")
|
||||
if err := c.initSites(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !c.h.quiet {
|
||||
c.Logger.FEEDBACK.Println("Started building sites ...")
|
||||
}
|
||||
return c.hugo.Build(hugolib.BuildCfg{CreateSitesFromConfig: true})
|
||||
}
|
||||
|
||||
func (c *commandeer) resetAndBuildSites() (err error) {
|
||||
if err = c.initSites(); err != nil {
|
||||
return
|
||||
}
|
||||
if !c.h.quiet {
|
||||
c.Logger.FEEDBACK.Println("Started building sites ...")
|
||||
}
|
||||
return c.hugo.Build(hugolib.BuildCfg{ResetState: true})
|
||||
}
|
||||
|
||||
func (c *commandeer) initSites() error {
|
||||
if c.hugo != nil {
|
||||
c.hugo.Cfg = c.Cfg
|
||||
c.hugo.Log.ResetLogCounters()
|
||||
return nil
|
||||
}
|
||||
|
||||
h, err := hugolib.NewHugoSites(*c.DepsCfg)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.hugo = h
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *commandeer) buildSites() (err error) {
|
||||
if err := c.initSites(); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.hugo.Build(hugolib.BuildCfg{})
|
||||
}
|
||||
|
||||
func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
|
||||
defer c.timeTrack(time.Now(), "Total")
|
||||
|
||||
if err := c.initSites(); err != nil {
|
||||
return err
|
||||
}
|
||||
visited := c.visitedURLs.PeekAllSet()
|
||||
doLiveReload := !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload")
|
||||
if doLiveReload && !c.Cfg.GetBool("disableFastRender") {
|
||||
|
||||
// Make sure we always render the home pages
|
||||
for _, l := range c.languages {
|
||||
langPath := c.PathSpec().GetLangSubDir(l.Lang)
|
||||
langPath := c.hugo.PathSpec.GetLangSubDir(l.Lang)
|
||||
if langPath != "" {
|
||||
langPath = langPath + "/"
|
||||
}
|
||||
home := c.pathSpec.PrependBasePath("/" + langPath)
|
||||
home := c.hugo.PathSpec.PrependBasePath("/" + langPath)
|
||||
visited[home] = true
|
||||
}
|
||||
|
||||
@@ -717,12 +634,22 @@ func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
|
||||
}
|
||||
|
||||
func (c *commandeer) fullRebuild() {
|
||||
if err := c.loadConfig(true); err != nil {
|
||||
c.commandeerHugoState = &commandeerHugoState{}
|
||||
err := c.loadConfig(true, true)
|
||||
if err != nil {
|
||||
jww.ERROR.Println("Failed to reload config:", err)
|
||||
} else if err := c.recreateAndBuildSites(true); err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
} else if !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload") {
|
||||
livereload.ForceRefresh()
|
||||
// Set the processing on pause until the state is recovered.
|
||||
c.paused = true
|
||||
} else {
|
||||
c.paused = false
|
||||
}
|
||||
|
||||
if !c.paused {
|
||||
if err := c.buildSites(); err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
} else if !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload") {
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,6 +689,35 @@ func (c *commandeer) newWatcher(dirList ...string) (*watcher.Batcher, error) {
|
||||
for {
|
||||
select {
|
||||
case evs := <-watcher.Events:
|
||||
for _, ev := range evs {
|
||||
if configSet[ev.Name] {
|
||||
if ev.Op&fsnotify.Chmod == fsnotify.Chmod {
|
||||
continue
|
||||
}
|
||||
if ev.Op&fsnotify.Remove == fsnotify.Remove {
|
||||
for _, configFile := range c.configFiles {
|
||||
counter := 0
|
||||
for watcher.Add(configFile) != nil {
|
||||
counter++
|
||||
if counter >= 100 {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Config file changed. Need full rebuild.
|
||||
c.fullRebuild()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if c.paused {
|
||||
// Wait for the server to get into a consistent state before
|
||||
// we continue with processing.
|
||||
continue
|
||||
}
|
||||
|
||||
if len(evs) > 50 {
|
||||
// This is probably a mass edit of the content dir.
|
||||
// Schedule a full rebuild for when it slows down.
|
||||
@@ -777,15 +733,6 @@ func (c *commandeer) newWatcher(dirList ...string) (*watcher.Batcher, error) {
|
||||
// Special handling for symbolic links inside /content.
|
||||
filtered := []fsnotify.Event{}
|
||||
for _, ev := range evs {
|
||||
if configSet[ev.Name] {
|
||||
if ev.Op&fsnotify.Chmod == fsnotify.Chmod {
|
||||
continue
|
||||
}
|
||||
// Config file changed. Need full rebuild.
|
||||
c.fullRebuild()
|
||||
break
|
||||
}
|
||||
|
||||
// Check the most specific first, i.e. files.
|
||||
contentMapped := c.hugo.ContentChanges.GetSymbolicLinkMappings(ev.Name)
|
||||
if len(contentMapped) > 0 {
|
||||
@@ -892,7 +839,7 @@ func (c *commandeer) newWatcher(dirList ...string) (*watcher.Batcher, error) {
|
||||
c.Logger.FEEDBACK.Printf("Syncing all static files\n")
|
||||
_, err := c.copyStatic()
|
||||
if err != nil {
|
||||
utils.StopOnErr(c.Logger, err, "Error copying static files to publish dir")
|
||||
stopOnErr(c.Logger, err, "Error copying static files to publish dir")
|
||||
}
|
||||
} else {
|
||||
if err := staticSyncer.syncsStaticEvents(staticEvents); err != nil {
|
||||
@@ -905,12 +852,11 @@ func (c *commandeer) newWatcher(dirList ...string) (*watcher.Batcher, error) {
|
||||
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
|
||||
|
||||
// force refresh when more than one file
|
||||
if len(staticEvents) > 0 {
|
||||
for _, ev := range staticEvents {
|
||||
path := staticSyncer.d.MakeStaticPathRelative(ev.Name)
|
||||
livereload.RefreshPath(path)
|
||||
}
|
||||
|
||||
if len(staticEvents) == 1 {
|
||||
ev := staticEvents[0]
|
||||
path := c.hugo.BaseFs.SourceFilesystems.MakeStaticPathRelative(ev.Name)
|
||||
path = c.firstPathSpec().RelURL(helpers.ToSlashTrimLeading(path), false)
|
||||
livereload.RefreshPath(path)
|
||||
} else {
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
@@ -918,34 +864,54 @@ func (c *commandeer) newWatcher(dirList ...string) (*watcher.Batcher, error) {
|
||||
}
|
||||
|
||||
if len(dynamicEvents) > 0 {
|
||||
partitionedEvents := partitionDynamicEvents(
|
||||
c.firstPathSpec().BaseFs.SourceFilesystems,
|
||||
dynamicEvents)
|
||||
|
||||
doLiveReload := !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload")
|
||||
onePageName := pickOneWriteOrCreatePath(dynamicEvents)
|
||||
onePageName := pickOneWriteOrCreatePath(partitionedEvents.ContentEvents)
|
||||
|
||||
c.Logger.FEEDBACK.Println("\nChange detected, rebuilding site")
|
||||
const layout = "2006-01-02 15:04:05.000 -0700"
|
||||
c.Logger.FEEDBACK.Println(time.Now().Format(layout))
|
||||
|
||||
c.changeDetector.PrepareNew()
|
||||
if err := c.rebuildSites(dynamicEvents); err != nil {
|
||||
c.Logger.ERROR.Println("Failed to rebuild site:", err)
|
||||
}
|
||||
|
||||
if doLiveReload {
|
||||
navigate := c.Cfg.GetBool("navigateToChanged")
|
||||
// We have fetched the same page above, but it may have
|
||||
// changed.
|
||||
var p *hugolib.Page
|
||||
|
||||
if navigate {
|
||||
if onePageName != "" {
|
||||
p = c.hugo.GetContentPage(onePageName)
|
||||
if len(partitionedEvents.ContentEvents) == 0 && len(partitionedEvents.AssetEvents) > 0 {
|
||||
changed := c.changeDetector.changed()
|
||||
if c.changeDetector != nil && len(changed) == 0 {
|
||||
// Nothing has changed.
|
||||
continue
|
||||
} else if len(changed) == 1 {
|
||||
pathToRefresh := c.firstPathSpec().RelURL(helpers.ToSlashTrimLeading(changed[0]), false)
|
||||
livereload.RefreshPath(pathToRefresh)
|
||||
} else {
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
livereload.NavigateToPathForPort(p.RelPermalink(), p.Site.ServerPort())
|
||||
} else {
|
||||
livereload.ForceRefresh()
|
||||
if len(partitionedEvents.ContentEvents) > 0 {
|
||||
|
||||
navigate := c.Cfg.GetBool("navigateToChanged")
|
||||
// We have fetched the same page above, but it may have
|
||||
// changed.
|
||||
var p *hugolib.Page
|
||||
|
||||
if navigate {
|
||||
if onePageName != "" {
|
||||
p = c.hugo.GetContentPage(onePageName)
|
||||
}
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
livereload.NavigateToPathForPort(p.RelPermalink(), p.Site.ServerPort())
|
||||
} else {
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -960,6 +926,26 @@ func (c *commandeer) newWatcher(dirList ...string) (*watcher.Batcher, error) {
|
||||
return watcher, nil
|
||||
}
|
||||
|
||||
// dynamicEvents contains events that is considered dynamic, as in "not static".
|
||||
// Both of these categories will trigger a new build, but the asset events
|
||||
// does not fit into the "navigate to changed" logic.
|
||||
type dynamicEvents struct {
|
||||
ContentEvents []fsnotify.Event
|
||||
AssetEvents []fsnotify.Event
|
||||
}
|
||||
|
||||
func partitionDynamicEvents(sourceFs *filesystems.SourceFilesystems, events []fsnotify.Event) (de dynamicEvents) {
|
||||
for _, e := range events {
|
||||
if sourceFs.IsAsset(e.Name) {
|
||||
de.AssetEvents = append(de.AssetEvents, e)
|
||||
} else {
|
||||
de.ContentEvents = append(de.ContentEvents, e)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func pickOneWriteOrCreatePath(events []fsnotify.Event) string {
|
||||
name := ""
|
||||
|
||||
@@ -976,32 +962,36 @@ func pickOneWriteOrCreatePath(events []fsnotify.Event) string {
|
||||
}
|
||||
|
||||
// isThemeVsHugoVersionMismatch returns whether the current Hugo version is
|
||||
// less than the theme's min_version.
|
||||
func (c *commandeer) isThemeVsHugoVersionMismatch(fs afero.Fs) (mismatch bool, requiredMinVersion string) {
|
||||
if !c.PathSpec().ThemeSet() {
|
||||
// less than any of the themes' min_version.
|
||||
func (c *commandeer) isThemeVsHugoVersionMismatch(fs afero.Fs) (dir string, mismatch bool, requiredMinVersion string) {
|
||||
if !c.hugo.PathSpec.ThemeSet() {
|
||||
return
|
||||
}
|
||||
|
||||
themeDir := c.PathSpec().GetThemeDir()
|
||||
for _, absThemeDir := range c.hugo.BaseFs.AbsThemeDirs {
|
||||
|
||||
path := filepath.Join(themeDir, "theme.toml")
|
||||
path := filepath.Join(absThemeDir, "theme.toml")
|
||||
|
||||
exists, err := helpers.Exists(path, fs)
|
||||
exists, err := helpers.Exists(path, fs)
|
||||
|
||||
if err != nil || !exists {
|
||||
return
|
||||
}
|
||||
if err != nil || !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
b, err := afero.ReadFile(fs, path)
|
||||
b, err := afero.ReadFile(fs, path)
|
||||
|
||||
tomlMeta, err := parser.HandleTOMLMetaData(b)
|
||||
tomlMeta, err := parser.HandleTOMLMetaData(b)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if minVersion, ok := tomlMeta["min_version"]; ok {
|
||||
if helpers.CompareVersion(minVersion) > 0 {
|
||||
return absThemeDir, true, fmt.Sprint(minVersion)
|
||||
}
|
||||
}
|
||||
|
||||
if minVersion, ok := tomlMeta["min_version"]; ok {
|
||||
return helpers.CompareVersion(minVersion) > 0, fmt.Sprint(minVersion)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -23,5 +23,5 @@ func init() {
|
||||
|
||||
You need to open cmd.exe and run Hugo from there.
|
||||
|
||||
Visit http://gohugo.io/ for more information.`
|
||||
Visit https://gohugo.io/ for more information.`
|
||||
}
|
||||
|
||||
@@ -73,23 +73,23 @@ Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root
|
||||
func (i *importCmd) importFromJekyll(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if len(args) < 2 {
|
||||
return newUserError(`Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.")
|
||||
return newUserError(`import from jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.")
|
||||
}
|
||||
|
||||
jekyllRoot, err := filepath.Abs(filepath.Clean(args[0]))
|
||||
if err != nil {
|
||||
return newUserError("Path error:", args[0])
|
||||
return newUserError("path error:", args[0])
|
||||
}
|
||||
|
||||
targetDir, err := filepath.Abs(filepath.Clean(args[1]))
|
||||
if err != nil {
|
||||
return newUserError("Path error:", args[1])
|
||||
return newUserError("path error:", args[1])
|
||||
}
|
||||
|
||||
jww.INFO.Println("Import Jekyll from:", jekyllRoot, "to:", targetDir)
|
||||
|
||||
if strings.HasPrefix(filepath.Dir(targetDir), jekyllRoot) {
|
||||
return newUserError("Target path should not be inside the Jekyll root, aborting.")
|
||||
return newUserError("abort: target path should not be inside the Jekyll root")
|
||||
}
|
||||
|
||||
forceImport, _ := cmd.Flags().GetBool("force")
|
||||
@@ -97,7 +97,7 @@ func (i *importCmd) importFromJekyll(cmd *cobra.Command, args []string) error {
|
||||
fs := afero.NewOsFs()
|
||||
jekyllPostDirs, hasAnyPost := i.getJekyllDirInfo(fs, jekyllRoot)
|
||||
if !hasAnyPost {
|
||||
return errors.New("Your Jekyll root contains neither posts nor drafts, aborting.")
|
||||
return errors.New("abort: jekyll root contains neither posts nor drafts")
|
||||
}
|
||||
|
||||
site, err := i.createSiteFromJekyll(jekyllRoot, targetDir, jekyllPostDirs, forceImport)
|
||||
@@ -120,7 +120,7 @@ func (i *importCmd) importFromJekyll(cmd *cobra.Command, args []string) error {
|
||||
|
||||
relPath, err := filepath.Rel(jekyllRoot, path)
|
||||
if err != nil {
|
||||
return newUserError("Get rel path error:", path)
|
||||
return newUserError("get rel path error:", path)
|
||||
}
|
||||
|
||||
relPath = filepath.ToSlash(relPath)
|
||||
@@ -204,13 +204,13 @@ func (i *importCmd) createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPos
|
||||
fs := s.Fs.Source
|
||||
if exists, _ := helpers.Exists(targetDir, fs); exists {
|
||||
if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
|
||||
return nil, errors.New("Target path \"" + targetDir + "\" already exists but not a directory")
|
||||
return nil, errors.New("target path \"" + targetDir + "\" exists but is not a directory")
|
||||
}
|
||||
|
||||
isEmpty, _ := helpers.IsEmpty(targetDir, fs)
|
||||
|
||||
if !isEmpty && !force {
|
||||
return nil, errors.New("Target path \"" + targetDir + "\" already exists and is not empty")
|
||||
return nil, errors.New("target path \"" + targetDir + "\" exists and is not empty")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -50,7 +50,7 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.",
|
||||
c.Set("buildDrafts", true)
|
||||
return nil
|
||||
}
|
||||
c, err := initializeConfig(false, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
c, err := initializeConfig(true, false, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -86,7 +86,7 @@ posted in the future.`,
|
||||
c.Set("buildFuture", true)
|
||||
return nil
|
||||
}
|
||||
c, err := initializeConfig(false, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
c, err := initializeConfig(true, false, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -122,7 +122,7 @@ expired.`,
|
||||
c.Set("buildExpired", true)
|
||||
return nil
|
||||
}
|
||||
c, err := initializeConfig(false, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
c, err := initializeConfig(true, false, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+11
-60
@@ -71,7 +71,7 @@ func (n *newCmd) newContent(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
c, err := initializeConfig(false, &n.hugoBuilderCommon, n, cfgInit)
|
||||
c, err := initializeConfig(true, false, &n.hugoBuilderCommon, n, cfgInit)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -85,69 +85,13 @@ func (n *newCmd) newContent(cmd *cobra.Command, args []string) error {
|
||||
|
||||
var kind string
|
||||
|
||||
createPath, kind = newContentPathSection(createPath)
|
||||
createPath, kind = newContentPathSection(c.hugo, createPath)
|
||||
|
||||
if n.contentType != "" {
|
||||
kind = n.contentType
|
||||
}
|
||||
|
||||
cfg := c.DepsCfg
|
||||
|
||||
ps, err := helpers.NewPathSpec(cfg.Fs, cfg.Cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If a site isn't in use in the archetype template, we can skip the build.
|
||||
siteFactory := func(filename string, siteUsed bool) (*hugolib.Site, error) {
|
||||
if !siteUsed {
|
||||
return hugolib.NewSite(*cfg)
|
||||
}
|
||||
var s *hugolib.Site
|
||||
if err := c.initSites(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.hugo.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s = c.hugo.Sites[0]
|
||||
|
||||
if len(c.hugo.Sites) > 1 {
|
||||
// Find the best match.
|
||||
for _, ss := range c.hugo.Sites {
|
||||
if strings.Contains(createPath, "."+ss.Language.Lang) {
|
||||
s = ss
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
return create.NewContent(ps, siteFactory, kind, createPath)
|
||||
}
|
||||
|
||||
func nextStepsText() string {
|
||||
var nextStepsText bytes.Buffer
|
||||
|
||||
nextStepsText.WriteString(`Just a few more steps and you're ready to go:
|
||||
|
||||
1. Download a theme into the same-named folder.
|
||||
Choose a theme from https://themes.gohugo.io/, or
|
||||
create your own with the "hugo new theme <THEMENAME>" command.
|
||||
2. Perhaps you want to add some content. You can add single files
|
||||
with "hugo new `)
|
||||
|
||||
nextStepsText.WriteString(filepath.Join("<SECTIONNAME>", "<FILENAME>.<FORMAT>"))
|
||||
|
||||
nextStepsText.WriteString(`".
|
||||
3. Start the built-in live server via "hugo server".
|
||||
|
||||
Visit https://gohugo.io/ for quickstart guide and full documentation.`)
|
||||
|
||||
return nextStepsText.String()
|
||||
return create.NewContent(c.hugo, kind, createPath)
|
||||
}
|
||||
|
||||
func mkdir(x ...string) {
|
||||
@@ -168,10 +112,17 @@ func touchFile(fs afero.Fs, x ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
func newContentPathSection(path string) (string, string) {
|
||||
func newContentPathSection(h *hugolib.HugoSites, path string) (string, string) {
|
||||
// Forward slashes is used in all examples. Convert if needed.
|
||||
// Issue #1133
|
||||
createpath := filepath.FromSlash(path)
|
||||
|
||||
if h != nil {
|
||||
for _, s := range h.Sites {
|
||||
createpath = strings.TrimPrefix(createpath, s.PathSpec.ContentDir)
|
||||
}
|
||||
}
|
||||
|
||||
var section string
|
||||
// assume the first directory is the section (kind)
|
||||
if strings.Contains(createpath[1:], helpers.FilePathSeparator) {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
|
||||
// Issue #1133
|
||||
func TestNewContentPathSectionWithForwardSlashes(t *testing.T) {
|
||||
p, s := newContentPathSection("/post/new.md")
|
||||
p, s := newContentPathSection(nil, "/post/new.md")
|
||||
assert.Equal(t, filepath.FromSlash("/post/new.md"), p)
|
||||
assert.Equal(t, "post", s)
|
||||
}
|
||||
|
||||
+26
-11
@@ -15,24 +15,18 @@ package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/create"
|
||||
|
||||
"fmt"
|
||||
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
|
||||
"github.com/gohugoio/hugo/create"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/spf13/cobra"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var _ cmder = (*newSiteCmd)(nil)
|
||||
@@ -104,7 +98,7 @@ func (n *newSiteCmd) doNewSite(fs *hugofs.Fs, basepath string, force bool) error
|
||||
|
||||
createConfig(fs, basepath, n.configFormat)
|
||||
|
||||
// Create a defaul archetype file.
|
||||
// Create a default archetype file.
|
||||
helpers.SafeWriteToDisk(filepath.Join(archeTypePath, "default.md"),
|
||||
strings.NewReader(create.ArchetypeTemplateTemplate), fs.Source)
|
||||
|
||||
@@ -146,3 +140,24 @@ func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
|
||||
|
||||
return helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), &buf, fs.Source)
|
||||
}
|
||||
|
||||
func nextStepsText() string {
|
||||
var nextStepsText bytes.Buffer
|
||||
|
||||
nextStepsText.WriteString(`Just a few more steps and you're ready to go:
|
||||
|
||||
1. Download a theme into the same-named folder.
|
||||
Choose a theme from https://themes.gohugo.io/, or
|
||||
create your own with the "hugo new theme <THEMENAME>" command.
|
||||
2. Perhaps you want to add some content. You can add single files
|
||||
with "hugo new `)
|
||||
|
||||
nextStepsText.WriteString(filepath.Join("<SECTIONNAME>", "<FILENAME>.<FORMAT>"))
|
||||
|
||||
nextStepsText.WriteString(`".
|
||||
3. Start the built-in live server via "hugo server".
|
||||
|
||||
Visit https://gohugo.io/ for quickstart guide and full documentation.`)
|
||||
|
||||
return nextStepsText.String()
|
||||
}
|
||||
|
||||
+29
-12
@@ -14,16 +14,14 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"bytes"
|
||||
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/spf13/cobra"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
@@ -53,8 +51,9 @@ as you see fit.`,
|
||||
return ccmd
|
||||
}
|
||||
|
||||
// newTheme creates a new Hugo theme template
|
||||
func (n *newThemeCmd) newTheme(cmd *cobra.Command, args []string) error {
|
||||
c, err := initializeConfig(false, &n.hugoBuilderCommon, n, nil)
|
||||
c, err := initializeConfig(false, false, &n.hugoBuilderCommon, n, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -64,13 +63,13 @@ func (n *newThemeCmd) newTheme(cmd *cobra.Command, args []string) error {
|
||||
return newUserError("theme name needs to be provided")
|
||||
}
|
||||
|
||||
createpath := c.PathSpec().AbsPathify(filepath.Join(c.Cfg.GetString("themesDir"), args[0]))
|
||||
jww.INFO.Println("creating theme at", createpath)
|
||||
createpath := c.hugo.PathSpec.AbsPathify(filepath.Join(c.Cfg.GetString("themesDir"), args[0]))
|
||||
jww.FEEDBACK.Println("Creating theme at", createpath)
|
||||
|
||||
cfg := c.DepsCfg
|
||||
|
||||
if x, _ := helpers.Exists(createpath, cfg.Fs.Source); x {
|
||||
return newUserError(createpath, "already exists")
|
||||
return errors.New(createpath + " already exists")
|
||||
}
|
||||
|
||||
mkdir(createpath, "layouts", "_default")
|
||||
@@ -81,6 +80,24 @@ func (n *newThemeCmd) newTheme(cmd *cobra.Command, args []string) error {
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "list.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "single.html")
|
||||
|
||||
baseofDefault := []byte(`<!DOCTYPE html>
|
||||
<html>
|
||||
{{- partial "head.html" . -}}
|
||||
<body>
|
||||
{{- partial "header.html" . -}}
|
||||
<div id="content">
|
||||
{{- block "main" . }}{{- end }}
|
||||
</div>
|
||||
{{- partial "footer.html" . -}}
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "layouts", "_default", "baseof.html"), bytes.NewReader(baseofDefault), cfg.Fs.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "head.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "header.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "footer.html")
|
||||
|
||||
@@ -118,7 +135,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), cfg.Fs.Source)
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE"), bytes.NewReader(by), cfg.Fs.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -135,12 +152,12 @@ func (n *newThemeCmd) createThemeMD(fs *hugofs.Fs, inpath string) (err error) {
|
||||
|
||||
name = "` + strings.Title(helpers.MakeTitle(filepath.Base(inpath))) + `"
|
||||
license = "MIT"
|
||||
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE.md"
|
||||
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE"
|
||||
description = ""
|
||||
homepage = "http://example.com/"
|
||||
tags = []
|
||||
features = []
|
||||
min_version = "0.38"
|
||||
min_version = "0.41"
|
||||
|
||||
[author]
|
||||
name = ""
|
||||
|
||||
+21
-21
@@ -122,27 +122,27 @@ func (f noDirFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
|
||||
var serverPorts []int
|
||||
|
||||
func (s *serverCmd) server(cmd *cobra.Command, args []string) error {
|
||||
func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
|
||||
// If a Destination is provided via flag write to disk
|
||||
destination, _ := cmd.Flags().GetString("destination")
|
||||
if destination != "" {
|
||||
s.renderToDisk = true
|
||||
sc.renderToDisk = true
|
||||
}
|
||||
|
||||
var serverCfgInit sync.Once
|
||||
|
||||
cfgInit := func(c *commandeer) error {
|
||||
c.Set("renderToMemory", !s.renderToDisk)
|
||||
c.Set("renderToMemory", !sc.renderToDisk)
|
||||
if cmd.Flags().Changed("navigateToChanged") {
|
||||
c.Set("navigateToChanged", s.navigateToChanged)
|
||||
c.Set("navigateToChanged", sc.navigateToChanged)
|
||||
}
|
||||
if cmd.Flags().Changed("disableLiveReload") {
|
||||
c.Set("disableLiveReload", s.disableLiveReload)
|
||||
c.Set("disableLiveReload", sc.disableLiveReload)
|
||||
}
|
||||
if cmd.Flags().Changed("disableFastRender") {
|
||||
c.Set("disableFastRender", s.disableFastRender)
|
||||
c.Set("disableFastRender", sc.disableFastRender)
|
||||
}
|
||||
if s.serverWatch {
|
||||
if sc.serverWatch {
|
||||
c.Set("watch", true)
|
||||
}
|
||||
|
||||
@@ -158,25 +158,25 @@ func (s *serverCmd) server(cmd *cobra.Command, args []string) error {
|
||||
serverPorts = make([]int, 1)
|
||||
|
||||
if c.languages.IsMultihost() {
|
||||
if !s.serverAppend {
|
||||
if !sc.serverAppend {
|
||||
err = newSystemError("--appendPort=false not supported when in multihost mode")
|
||||
}
|
||||
serverPorts = make([]int, len(c.languages))
|
||||
}
|
||||
|
||||
currentServerPort := s.serverPort
|
||||
currentServerPort := sc.serverPort
|
||||
|
||||
for i := 0; i < len(serverPorts); i++ {
|
||||
l, err := net.Listen("tcp", net.JoinHostPort(s.serverInterface, strconv.Itoa(currentServerPort)))
|
||||
l, err := net.Listen("tcp", net.JoinHostPort(sc.serverInterface, strconv.Itoa(currentServerPort)))
|
||||
if err == nil {
|
||||
l.Close()
|
||||
serverPorts[i] = currentServerPort
|
||||
} else {
|
||||
if i == 0 && s.cmd.Flags().Changed("port") {
|
||||
if i == 0 && sc.cmd.Flags().Changed("port") {
|
||||
// port set explicitly by user -- he/she probably meant it!
|
||||
err = newSystemErrorF("Server startup failed: %s", err)
|
||||
}
|
||||
jww.ERROR.Println("port", s.serverPort, "already in use, attempting to use an available port")
|
||||
jww.ERROR.Println("port", sc.serverPort, "already in use, attempting to use an available port")
|
||||
sp, err := helpers.FindAvailablePort()
|
||||
if err != nil {
|
||||
err = newSystemError("Unable to find alternative port to use:", err)
|
||||
@@ -190,9 +190,9 @@ func (s *serverCmd) server(cmd *cobra.Command, args []string) error {
|
||||
|
||||
c.serverPorts = serverPorts
|
||||
|
||||
c.Set("port", s.serverPort)
|
||||
if s.liveReloadPort != -1 {
|
||||
c.Set("liveReloadPort", s.liveReloadPort)
|
||||
c.Set("port", sc.serverPort)
|
||||
if sc.liveReloadPort != -1 {
|
||||
c.Set("liveReloadPort", sc.liveReloadPort)
|
||||
} else {
|
||||
c.Set("liveReloadPort", serverPorts[0])
|
||||
}
|
||||
@@ -206,7 +206,7 @@ func (s *serverCmd) server(cmd *cobra.Command, args []string) error {
|
||||
serverPort = serverPorts[0]
|
||||
}
|
||||
|
||||
baseURL, err := s.fixURL(language, s.baseURL, serverPort)
|
||||
baseURL, err := sc.fixURL(language, sc.baseURL, serverPort)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -226,7 +226,7 @@ func (s *serverCmd) server(cmd *cobra.Command, args []string) error {
|
||||
jww.ERROR.Println("memstats error:", err)
|
||||
}
|
||||
|
||||
c, err := initializeConfig(true, &s.hugoBuilderCommon, s, cfgInit)
|
||||
c, err := initializeConfig(true, true, &sc.hugoBuilderCommon, sc, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -240,7 +240,7 @@ func (s *serverCmd) server(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Watch runs its own server as part of the routine
|
||||
if s.serverWatch {
|
||||
if sc.serverWatch {
|
||||
|
||||
watchDirs, err := c.getDirList()
|
||||
if err != nil {
|
||||
@@ -266,7 +266,7 @@ func (s *serverCmd) server(cmd *cobra.Command, args []string) error {
|
||||
|
||||
}
|
||||
|
||||
return c.serve(s)
|
||||
return c.serve(sc)
|
||||
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
|
||||
publishDir = filepath.Join(publishDir, root)
|
||||
}
|
||||
|
||||
absPublishDir := f.c.PathSpec().AbsPathify(publishDir)
|
||||
absPublishDir := f.c.hugo.PathSpec.AbsPathify(publishDir)
|
||||
|
||||
if i == 0 {
|
||||
if f.s.renderToDisk {
|
||||
@@ -298,7 +298,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
|
||||
}
|
||||
}
|
||||
|
||||
httpFs := afero.NewHttpFs(f.c.Fs.Destination)
|
||||
httpFs := afero.NewHttpFs(f.c.destinationFs)
|
||||
fs := filesOnlyFs{httpFs.Dir(absPublishDir)}
|
||||
|
||||
doLiveReload := !f.s.buildWatch && !f.c.Cfg.GetBool("disableLiveReload")
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -27,6 +28,10 @@ import (
|
||||
)
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
if isWindowsCI() {
|
||||
// TODO(bep) not sure why server tests have started to fail on the Windows CI server.
|
||||
t.Skip("Skip server test on appveyor")
|
||||
}
|
||||
assert := require.New(t)
|
||||
dir, err := createSimpleTestSite(t)
|
||||
assert.NoError(err)
|
||||
@@ -107,3 +112,7 @@ func TestFixURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isWindowsCI() bool {
|
||||
return runtime.GOOS == "windows" && os.Getenv("CI") != ""
|
||||
}
|
||||
|
||||
+15
-26
@@ -17,53 +17,43 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib/filesystems"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
src "github.com/gohugoio/hugo/source"
|
||||
"github.com/spf13/fsync"
|
||||
)
|
||||
|
||||
type staticSyncer struct {
|
||||
c *commandeer
|
||||
d *src.Dirs
|
||||
}
|
||||
|
||||
func newStaticSyncer(c *commandeer) (*staticSyncer, error) {
|
||||
dirs, err := src.NewDirs(c.Fs, c.Cfg, c.DepsCfg.Logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &staticSyncer{c: c, d: dirs}, nil
|
||||
return &staticSyncer{c: c}, nil
|
||||
}
|
||||
|
||||
func (s *staticSyncer) isStatic(path string) bool {
|
||||
return s.d.IsStatic(path)
|
||||
func (s *staticSyncer) isStatic(filename string) bool {
|
||||
return s.c.hugo.BaseFs.SourceFilesystems.IsStatic(filename)
|
||||
}
|
||||
|
||||
func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
|
||||
c := s.c
|
||||
|
||||
syncFn := func(dirs *src.Dirs, publishDir string) (uint64, error) {
|
||||
staticSourceFs, err := dirs.CreateStaticFs()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
syncFn := func(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
|
||||
publishDir := c.hugo.PathSpec.PublishDir
|
||||
// If root, remove the second '/'
|
||||
if publishDir == "//" {
|
||||
publishDir = helpers.FilePathSeparator
|
||||
}
|
||||
|
||||
if dirs.Language != nil {
|
||||
// Multihost setup
|
||||
publishDir = filepath.Join(publishDir, dirs.Language.Lang)
|
||||
}
|
||||
|
||||
if staticSourceFs == nil {
|
||||
c.Logger.WARN.Println("No static directories found to sync")
|
||||
return 0, nil
|
||||
if sourceFs.PublishFolder != "" {
|
||||
publishDir = filepath.Join(publishDir, sourceFs.PublishFolder)
|
||||
}
|
||||
|
||||
syncer := fsync.NewSyncer()
|
||||
syncer.NoTimes = c.Cfg.GetBool("noTimes")
|
||||
syncer.NoChmod = c.Cfg.GetBool("noChmod")
|
||||
syncer.SrcFs = staticSourceFs
|
||||
syncer.SrcFs = sourceFs.Fs
|
||||
syncer.DestFs = c.Fs.Destination
|
||||
|
||||
// prevent spamming the log on changes
|
||||
@@ -88,8 +78,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
|
||||
|
||||
fromPath := ev.Name
|
||||
|
||||
// If we are here we already know the event took place in a static dir
|
||||
relPath := dirs.MakeStaticPathRelative(fromPath)
|
||||
relPath := sourceFs.MakePathRelative(fromPath)
|
||||
if relPath == "" {
|
||||
// Not member of this virtual host.
|
||||
continue
|
||||
@@ -105,7 +94,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
|
||||
// the source of that static file. In this case Hugo will incorrectly remove that file
|
||||
// from the published directory.
|
||||
if ev.Op&fsnotify.Rename == fsnotify.Rename || ev.Op&fsnotify.Remove == fsnotify.Remove {
|
||||
if _, err := staticSourceFs.Stat(relPath); os.IsNotExist(err) {
|
||||
if _, err := sourceFs.Fs.Stat(relPath); os.IsNotExist(err) {
|
||||
// If file doesn't exist in any static dir, remove it
|
||||
toRemove := filepath.Join(publishDir, relPath)
|
||||
|
||||
|
||||
+20
-4
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/resource/tocss/scss"
|
||||
"github.com/spf13/cobra"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
@@ -44,9 +45,24 @@ func newVersionCmd() *versionCmd {
|
||||
}
|
||||
|
||||
func printHugoVersion() {
|
||||
if hugolib.CommitHash == "" {
|
||||
jww.FEEDBACK.Printf("Hugo Static Site Generator v%s %s/%s BuildDate: %s\n", helpers.CurrentHugoVersion, runtime.GOOS, runtime.GOARCH, hugolib.BuildDate)
|
||||
} else {
|
||||
jww.FEEDBACK.Printf("Hugo Static Site Generator v%s-%s %s/%s BuildDate: %s\n", helpers.CurrentHugoVersion, strings.ToUpper(hugolib.CommitHash), runtime.GOOS, runtime.GOARCH, hugolib.BuildDate)
|
||||
program := "Hugo Static Site Generator"
|
||||
|
||||
version := "v" + helpers.CurrentHugoVersion.String()
|
||||
if hugolib.CommitHash != "" {
|
||||
version += "-" + strings.ToUpper(hugolib.CommitHash)
|
||||
}
|
||||
if scss.Supports() {
|
||||
version += "/extended"
|
||||
}
|
||||
|
||||
osArch := runtime.GOOS + "/" + runtime.GOARCH
|
||||
|
||||
var buildDate string
|
||||
if hugolib.BuildDate != "" {
|
||||
buildDate = hugolib.BuildDate
|
||||
} else {
|
||||
buildDate = "unknown"
|
||||
}
|
||||
|
||||
jww.FEEDBACK.Println(program, version, osArch, "BuildDate:", buildDate)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 collections contains common Hugo functionality related to collection
|
||||
// handling.
|
||||
package collections
|
||||
|
||||
// Grouper defines a very generic way to group items by a given key.
|
||||
type Grouper interface {
|
||||
Group(key interface{}, items interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
// Slicer definse a very generic way to create a typed slice. This is used
|
||||
// in collections.Slice template func to get types such as Pages, PageGroups etc.
|
||||
// instead of the less useful []interface{}.
|
||||
type Slicer interface {
|
||||
Slice(items interface{}) (interface{}, error)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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 errors contains common Hugo errors and error related utilities.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// 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")
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 hugio
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ReadSeeker wraps io.Reader and io.Seeker.
|
||||
type ReadSeeker interface {
|
||||
io.Reader
|
||||
io.Seeker
|
||||
}
|
||||
|
||||
// ReadSeekCloser is implemented by afero.File. We use this as the common type for
|
||||
// content in Resource objects, even for strings.
|
||||
type ReadSeekCloser interface {
|
||||
ReadSeeker
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// ReadSeekerNoOpCloser implements ReadSeekCloser by doing nothing in Close.
|
||||
type ReadSeekerNoOpCloser struct {
|
||||
ReadSeeker
|
||||
}
|
||||
|
||||
// Close does nothing.
|
||||
func (r ReadSeekerNoOpCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewReadSeekerNoOpCloser creates a new ReadSeekerNoOpCloser with the given ReadSeeker.
|
||||
func NewReadSeekerNoOpCloser(r ReadSeeker) ReadSeekerNoOpCloser {
|
||||
return ReadSeekerNoOpCloser{r}
|
||||
}
|
||||
|
||||
// NewReadSeekerNoOpCloserFromString uses strings.NewReader to create a new ReadSeekerNoOpCloser
|
||||
// from the given string.
|
||||
func NewReadSeekerNoOpCloserFromString(content string) ReadSeekerNoOpCloser {
|
||||
return ReadSeekerNoOpCloser{strings.NewReader(content)}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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 hugio
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type multiWriteCloser struct {
|
||||
io.Writer
|
||||
closers []io.WriteCloser
|
||||
}
|
||||
|
||||
func (m multiWriteCloser) Close() error {
|
||||
var err error
|
||||
for _, c := range m.closers {
|
||||
if closeErr := c.Close(); err != nil {
|
||||
err = closeErr
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NewMultiWriteCloser creates a new io.WriteCloser that duplicates its writes to all the
|
||||
// provided writers.
|
||||
func NewMultiWriteCloser(writeClosers ...io.WriteCloser) io.WriteCloser {
|
||||
writers := make([]io.Writer, len(writeClosers))
|
||||
for i, w := range writeClosers {
|
||||
writers[i] = w
|
||||
}
|
||||
return multiWriteCloser{Writer: io.MultiWriter(writers...), closers: writeClosers}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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 (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
// NewDebugLogger is a convenience function to create a debug logger.
|
||||
func NewDebugLogger() *jww.Notepad {
|
||||
return jww.NewNotepad(jww.LevelDebug, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
|
||||
}
|
||||
|
||||
// NewWarningLogger is a convenience function to create a warning logger.
|
||||
func NewWarningLogger() *jww.Notepad {
|
||||
return jww.NewNotepad(jww.LevelWarn, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
|
||||
}
|
||||
|
||||
// NewErrorLogger is a convenience function to create an error logger.
|
||||
func NewErrorLogger() *jww.Notepad {
|
||||
return jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 maps
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// ToLower makes all the keys in the given map lower cased and will do so
|
||||
// recursively.
|
||||
// Notes:
|
||||
// * This will modify the map given.
|
||||
// * Any nested map[interface{}]interface{} will be converted to map[string]interface{}.
|
||||
func ToLower(m map[string]interface{}) {
|
||||
for k, v := range m {
|
||||
switch v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
v = cast.ToStringMap(v)
|
||||
ToLower(v.(map[string]interface{}))
|
||||
case map[string]interface{}:
|
||||
ToLower(v.(map[string]interface{}))
|
||||
}
|
||||
|
||||
lKey := strings.ToLower(k)
|
||||
if k != lKey {
|
||||
delete(m, k)
|
||||
m[lKey] = v
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 maps
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToLower(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
input map[string]interface{}
|
||||
expected map[string]interface{}
|
||||
}{
|
||||
{
|
||||
map[string]interface{}{
|
||||
"abC": 32,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"abc": 32,
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"abC": 32,
|
||||
"deF": map[interface{}]interface{}{
|
||||
23: "A value",
|
||||
24: map[string]interface{}{
|
||||
"AbCDe": "A value",
|
||||
"eFgHi": "Another value",
|
||||
},
|
||||
},
|
||||
"gHi": map[string]interface{}{
|
||||
"J": 25,
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"abc": 32,
|
||||
"def": map[string]interface{}{
|
||||
"23": "A value",
|
||||
"24": map[string]interface{}{
|
||||
"abcde": "A value",
|
||||
"efghi": "Another value",
|
||||
},
|
||||
},
|
||||
"ghi": map[string]interface{}{
|
||||
"j": 25,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
// ToLower modifies input.
|
||||
ToLower(test.input)
|
||||
if !reflect.DeepEqual(test.expected, test.input) {
|
||||
t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2015 The Hugo Authors. All rights reserved.
|
||||
// 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.
|
||||
@@ -11,14 +11,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hugolib
|
||||
package maps
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/gohugoio/hugo/tpl/math"
|
||||
"github.com/gohugoio/hugo/common/math"
|
||||
)
|
||||
|
||||
// Scratch is a writable context used for stateful operations in Page/Node rendering.
|
||||
@@ -73,7 +73,7 @@ func (c *Scratch) Set(key string, value interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Reset deletes the given key
|
||||
// Delete deletes the given key.
|
||||
func (c *Scratch) Delete(key string) string {
|
||||
c.mu.Lock()
|
||||
delete(c.values, key)
|
||||
@@ -81,7 +81,7 @@ func (c *Scratch) Delete(key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Get returns a value previously set by Add or Set
|
||||
// Get returns a value previously set by Add or Set.
|
||||
func (c *Scratch) Get(key string) interface{} {
|
||||
c.mu.RLock()
|
||||
val := c.values[key]
|
||||
@@ -104,7 +104,7 @@ func (c *Scratch) SetInMap(key string, mapKey string, value interface{}) string
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetSortedMapValues returns a sorted map previously filled with SetInMap
|
||||
// GetSortedMapValues returns a sorted map previously filled with SetInMap.
|
||||
func (c *Scratch) GetSortedMapValues(key string) interface{} {
|
||||
c.mu.RLock()
|
||||
|
||||
@@ -130,6 +130,7 @@ func (c *Scratch) GetSortedMapValues(key string) interface{} {
|
||||
return sortedArray
|
||||
}
|
||||
|
||||
func newScratch() *Scratch {
|
||||
// NewScratch returns a new instance Scratch.
|
||||
func NewScratch() *Scratch {
|
||||
return &Scratch{values: make(map[string]interface{})}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2015 The Hugo Authors. All rights reserved.
|
||||
// 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.
|
||||
@@ -11,7 +11,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hugolib
|
||||
package maps
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
|
||||
func TestScratchAdd(t *testing.T) {
|
||||
t.Parallel()
|
||||
scratch := newScratch()
|
||||
scratch := NewScratch()
|
||||
scratch.Add("int1", 10)
|
||||
scratch.Add("int1", 20)
|
||||
scratch.Add("int2", 20)
|
||||
@@ -53,7 +53,7 @@ func TestScratchAdd(t *testing.T) {
|
||||
|
||||
func TestScratchAddSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
scratch := newScratch()
|
||||
scratch := NewScratch()
|
||||
|
||||
_, err := scratch.Add("intSlice", []int{1, 2})
|
||||
assert.Nil(t, err)
|
||||
@@ -82,14 +82,14 @@ func TestScratchAddSlice(t *testing.T) {
|
||||
|
||||
func TestScratchSet(t *testing.T) {
|
||||
t.Parallel()
|
||||
scratch := newScratch()
|
||||
scratch := NewScratch()
|
||||
scratch.Set("key", "val")
|
||||
assert.Equal(t, "val", scratch.Get("key"))
|
||||
}
|
||||
|
||||
func TestScratchDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
scratch := newScratch()
|
||||
scratch := NewScratch()
|
||||
scratch.Set("key", "val")
|
||||
scratch.Delete("key")
|
||||
scratch.Add("key", "Lucy Parsons")
|
||||
@@ -99,7 +99,7 @@ func TestScratchDelete(t *testing.T) {
|
||||
// Issue #2005
|
||||
func TestScratchInParallel(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
scratch := newScratch()
|
||||
scratch := NewScratch()
|
||||
key := "counter"
|
||||
scratch.Set(key, int64(1))
|
||||
for i := 1; i <= 10; i++ {
|
||||
@@ -133,7 +133,7 @@ func TestScratchInParallel(t *testing.T) {
|
||||
|
||||
func TestScratchGet(t *testing.T) {
|
||||
t.Parallel()
|
||||
scratch := newScratch()
|
||||
scratch := NewScratch()
|
||||
nothing := scratch.Get("nothing")
|
||||
if nothing != nil {
|
||||
t.Errorf("Should not return anything, but got %v", nothing)
|
||||
@@ -142,7 +142,7 @@ func TestScratchGet(t *testing.T) {
|
||||
|
||||
func TestScratchSetInMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
scratch := newScratch()
|
||||
scratch := NewScratch()
|
||||
scratch.SetInMap("key", "lux", "Lux")
|
||||
scratch.SetInMap("key", "abc", "Abc")
|
||||
scratch.SetInMap("key", "zyx", "Zyx")
|
||||
@@ -153,7 +153,7 @@ func TestScratchSetInMap(t *testing.T) {
|
||||
|
||||
func TestScratchGetSortedMapValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
scratch := newScratch()
|
||||
scratch := NewScratch()
|
||||
nothing := scratch.GetSortedMapValues("nothing")
|
||||
if nothing != nil {
|
||||
t.Errorf("Should not return anything, but got %v", nothing)
|
||||
@@ -161,7 +161,7 @@ func TestScratchGetSortedMapValues(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkScratchGet(b *testing.B) {
|
||||
scratch := newScratch()
|
||||
scratch := NewScratch()
|
||||
scratch.Add("A", 1)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -0,0 +1,135 @@
|
||||
// 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 math
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// DoArithmetic performs arithmetic operations (+,-,*,/) using reflection to
|
||||
// determine the type of the two terms.
|
||||
func DoArithmetic(a, b interface{}, op rune) (interface{}, error) {
|
||||
av := reflect.ValueOf(a)
|
||||
bv := reflect.ValueOf(b)
|
||||
var ai, bi int64
|
||||
var af, bf float64
|
||||
var au, bu uint64
|
||||
switch av.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
ai = av.Int()
|
||||
switch bv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
bi = bv.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
af = float64(ai) // may overflow
|
||||
ai = 0
|
||||
bf = bv.Float()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
bu = bv.Uint()
|
||||
if ai >= 0 {
|
||||
au = uint64(ai)
|
||||
ai = 0
|
||||
} else {
|
||||
bi = int64(bu) // may overflow
|
||||
bu = 0
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("Can't apply the operator to the values")
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
af = av.Float()
|
||||
switch bv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
bf = float64(bv.Int()) // may overflow
|
||||
case reflect.Float32, reflect.Float64:
|
||||
bf = bv.Float()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
bf = float64(bv.Uint()) // may overflow
|
||||
default:
|
||||
return nil, errors.New("Can't apply the operator to the values")
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
au = av.Uint()
|
||||
switch bv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
bi = bv.Int()
|
||||
if bi >= 0 {
|
||||
bu = uint64(bi)
|
||||
bi = 0
|
||||
} else {
|
||||
ai = int64(au) // may overflow
|
||||
au = 0
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
af = float64(au) // may overflow
|
||||
au = 0
|
||||
bf = bv.Float()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
bu = bv.Uint()
|
||||
default:
|
||||
return nil, errors.New("Can't apply the operator to the values")
|
||||
}
|
||||
case reflect.String:
|
||||
as := av.String()
|
||||
if bv.Kind() == reflect.String && op == '+' {
|
||||
bs := bv.String()
|
||||
return as + bs, nil
|
||||
}
|
||||
return nil, errors.New("Can't apply the operator to the values")
|
||||
default:
|
||||
return nil, errors.New("Can't apply the operator to the values")
|
||||
}
|
||||
|
||||
switch op {
|
||||
case '+':
|
||||
if ai != 0 || bi != 0 {
|
||||
return ai + bi, nil
|
||||
} else if af != 0 || bf != 0 {
|
||||
return af + bf, nil
|
||||
} else if au != 0 || bu != 0 {
|
||||
return au + bu, nil
|
||||
}
|
||||
return 0, nil
|
||||
case '-':
|
||||
if ai != 0 || bi != 0 {
|
||||
return ai - bi, nil
|
||||
} else if af != 0 || bf != 0 {
|
||||
return af - bf, nil
|
||||
} else if au != 0 || bu != 0 {
|
||||
return au - bu, nil
|
||||
}
|
||||
return 0, nil
|
||||
case '*':
|
||||
if ai != 0 || bi != 0 {
|
||||
return ai * bi, nil
|
||||
} else if af != 0 || bf != 0 {
|
||||
return af * bf, nil
|
||||
} else if au != 0 || bu != 0 {
|
||||
return au * bu, nil
|
||||
}
|
||||
return 0, nil
|
||||
case '/':
|
||||
if bi != 0 {
|
||||
return ai / bi, nil
|
||||
} else if bf != 0 {
|
||||
return af / bf, nil
|
||||
} else if bu != 0 {
|
||||
return au / bu, nil
|
||||
}
|
||||
return nil, errors.New("Can't divide the value by 0")
|
||||
default:
|
||||
return nil, errors.New("There is no such an operation")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// 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 math
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDoArithmetic(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for i, test := range []struct {
|
||||
a interface{}
|
||||
b interface{}
|
||||
op rune
|
||||
expect interface{}
|
||||
}{
|
||||
{3, 2, '+', int64(5)},
|
||||
{3, 2, '-', int64(1)},
|
||||
{3, 2, '*', int64(6)},
|
||||
{3, 2, '/', int64(1)},
|
||||
{3.0, 2, '+', float64(5)},
|
||||
{3.0, 2, '-', float64(1)},
|
||||
{3.0, 2, '*', float64(6)},
|
||||
{3.0, 2, '/', float64(1.5)},
|
||||
{3, 2.0, '+', float64(5)},
|
||||
{3, 2.0, '-', float64(1)},
|
||||
{3, 2.0, '*', float64(6)},
|
||||
{3, 2.0, '/', float64(1.5)},
|
||||
{3.0, 2.0, '+', float64(5)},
|
||||
{3.0, 2.0, '-', float64(1)},
|
||||
{3.0, 2.0, '*', float64(6)},
|
||||
{3.0, 2.0, '/', float64(1.5)},
|
||||
{uint(3), uint(2), '+', uint64(5)},
|
||||
{uint(3), uint(2), '-', uint64(1)},
|
||||
{uint(3), uint(2), '*', uint64(6)},
|
||||
{uint(3), uint(2), '/', uint64(1)},
|
||||
{uint(3), 2, '+', uint64(5)},
|
||||
{uint(3), 2, '-', uint64(1)},
|
||||
{uint(3), 2, '*', uint64(6)},
|
||||
{uint(3), 2, '/', uint64(1)},
|
||||
{3, uint(2), '+', uint64(5)},
|
||||
{3, uint(2), '-', uint64(1)},
|
||||
{3, uint(2), '*', uint64(6)},
|
||||
{3, uint(2), '/', uint64(1)},
|
||||
{uint(3), -2, '+', int64(1)},
|
||||
{uint(3), -2, '-', int64(5)},
|
||||
{uint(3), -2, '*', int64(-6)},
|
||||
{uint(3), -2, '/', int64(-1)},
|
||||
{-3, uint(2), '+', int64(-1)},
|
||||
{-3, uint(2), '-', int64(-5)},
|
||||
{-3, uint(2), '*', int64(-6)},
|
||||
{-3, uint(2), '/', int64(-1)},
|
||||
{uint(3), 2.0, '+', float64(5)},
|
||||
{uint(3), 2.0, '-', float64(1)},
|
||||
{uint(3), 2.0, '*', float64(6)},
|
||||
{uint(3), 2.0, '/', float64(1.5)},
|
||||
{3.0, uint(2), '+', float64(5)},
|
||||
{3.0, uint(2), '-', float64(1)},
|
||||
{3.0, uint(2), '*', float64(6)},
|
||||
{3.0, uint(2), '/', float64(1.5)},
|
||||
{0, 0, '+', 0},
|
||||
{0, 0, '-', 0},
|
||||
{0, 0, '*', 0},
|
||||
{"foo", "bar", '+', "foobar"},
|
||||
{3, 0, '/', false},
|
||||
{3.0, 0, '/', false},
|
||||
{3, 0.0, '/', false},
|
||||
{uint(3), uint(0), '/', false},
|
||||
{3, uint(0), '/', false},
|
||||
{-3, uint(0), '/', false},
|
||||
{uint(3), 0, '/', false},
|
||||
{3.0, uint(0), '/', false},
|
||||
{uint(3), 0.0, '/', false},
|
||||
{3, "foo", '+', false},
|
||||
{3.0, "foo", '+', false},
|
||||
{uint(3), "foo", '+', false},
|
||||
{"foo", 3, '+', false},
|
||||
{"foo", "bar", '-', false},
|
||||
{3, 2, '%', false},
|
||||
} {
|
||||
errMsg := fmt.Sprintf("[%d] %v", i, test)
|
||||
|
||||
result, err := DoArithmetic(test.a, test.b, test.op)
|
||||
|
||||
if b, ok := test.expect.(bool); ok && !b {
|
||||
require.Error(t, err, errMsg)
|
||||
continue
|
||||
}
|
||||
|
||||
require.NoError(t, err, errMsg)
|
||||
assert.Equal(t, test.expect, result, errMsg)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
// 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.
|
||||
@@ -41,6 +41,8 @@ func (k KeyValues) String() string {
|
||||
return fmt.Sprintf("%v: %v", k.Key, k.Values)
|
||||
}
|
||||
|
||||
// NewKeyValuesStrings takes a given key and slice of values and returns a new
|
||||
// KeyValues struct.
|
||||
func NewKeyValuesStrings(key string, values ...string) KeyValues {
|
||||
iv := make([]interface{}, len(values))
|
||||
for i := 0; i < len(values); i++ {
|
||||
|
||||
@@ -13,6 +13,14 @@
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Provider provides the configuration settings for Hugo.
|
||||
type Provider interface {
|
||||
GetString(key string) string
|
||||
@@ -25,3 +33,24 @@ type Provider interface {
|
||||
Set(key string, value interface{})
|
||||
IsSet(key string) bool
|
||||
}
|
||||
|
||||
// FromConfigString creates a config from the given YAML, JSON or TOML config. This is useful in tests.
|
||||
func FromConfigString(config, configType string) (Provider, error) {
|
||||
v := viper.New()
|
||||
v.SetConfigType(configType)
|
||||
if err := v.ReadConfig(strings.NewReader(config)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// GetStringSlicePreserveString returns a string slice from the given config and key.
|
||||
// It differs from the GetStringSlice method in that if the config value is a string,
|
||||
// we do not attempt to split it into fields.
|
||||
func GetStringSlicePreserveString(cfg Provider, key string) []string {
|
||||
sd := cfg.Get(key)
|
||||
if sds, ok := sd.(string); ok {
|
||||
return []string{sds}
|
||||
}
|
||||
return cast.ToStringSlice(sd)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetStringSlicePreserveString(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
cfg := viper.New()
|
||||
|
||||
s := "This is a string"
|
||||
sSlice := []string{"This", "is", "a", "slice"}
|
||||
|
||||
cfg.Set("s1", s)
|
||||
cfg.Set("s2", sSlice)
|
||||
|
||||
assert.Equal([]string{s}, GetStringSlicePreserveString(cfg, "s1"))
|
||||
assert.Equal(sSlice, GetStringSlicePreserveString(cfg, "s2"))
|
||||
assert.Nil(GetStringSlicePreserveString(cfg, "s3"))
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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 privacy
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
const privacyConfigKey = "privacy"
|
||||
|
||||
// Service is the common values for a service in a policy definition.
|
||||
type Service struct {
|
||||
Disable bool
|
||||
}
|
||||
|
||||
// Config is a privacy configuration for all the relevant services in Hugo.
|
||||
type Config struct {
|
||||
Disqus Disqus
|
||||
GoogleAnalytics GoogleAnalytics
|
||||
Instagram Instagram
|
||||
Twitter Twitter
|
||||
Vimeo Vimeo
|
||||
YouTube YouTube
|
||||
}
|
||||
|
||||
// Disqus holds the privacy configuration settings related to the Disqus template.
|
||||
type Disqus struct {
|
||||
Service `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
// GoogleAnalytics holds the privacy configuration settings related to the Google Analytics template.
|
||||
type GoogleAnalytics struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
// Enabling this will disable the use of Cookies and use Session Storage to Store the GA Client ID.
|
||||
UseSessionStorage bool
|
||||
|
||||
// Enabling this will make the GA templates respect the
|
||||
// "Do Not Track" HTTP header. See https://www.paulfurley.com/google-analytics-dnt/.
|
||||
RespectDoNotTrack bool
|
||||
|
||||
// Enabling this will make it so the users' IP addresses are anonymized within Google Analytics.
|
||||
AnonymizeIP bool
|
||||
}
|
||||
|
||||
// Instagram holds the privacy configuration settings related to the Instagram shortcode.
|
||||
type Instagram struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
// If simple mode is enabled, a static and no-JS version of the Instagram
|
||||
// image card will be built.
|
||||
Simple bool
|
||||
}
|
||||
|
||||
// Twitter holds the privacy configuration settingsrelated to the Twitter shortcode.
|
||||
type Twitter struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
// When set to true, the Tweet and its embedded page on your site are not used
|
||||
// for purposes that include personalized suggestions and personalized ads.
|
||||
EnableDNT bool
|
||||
|
||||
// If simple mode is enabled, a static and no-JS version of the Tweet will be built.
|
||||
Simple bool
|
||||
}
|
||||
|
||||
// Vimeo holds the privacy configuration settingsrelated to the Vimeo shortcode.
|
||||
type Vimeo struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
// If simple mode is enabled, only a thumbnail is fetched from i.vimeocdn.com and
|
||||
// shown with a play button overlaid. If a user clicks the button, he/she will
|
||||
// be taken to the video page on vimeo.com in a new browser tab.
|
||||
Simple bool
|
||||
}
|
||||
|
||||
// YouTube holds the privacy configuration settingsrelated to the YouTube shortcode.
|
||||
type YouTube struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
// When you turn on privacy-enhanced mode,
|
||||
// YouTube won’t store information about visitors on your website
|
||||
// unless the user plays the embedded video.
|
||||
PrivacyEnhanced bool
|
||||
}
|
||||
|
||||
// DecodeConfig creates a privacy Config from a given Hugo configuration.
|
||||
func DecodeConfig(cfg config.Provider) (pc Config, err error) {
|
||||
if !cfg.IsSet(privacyConfigKey) {
|
||||
return
|
||||
}
|
||||
|
||||
m := cfg.GetStringMap(privacyConfigKey)
|
||||
|
||||
err = mapstructure.WeakDecode(m, &pc)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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 privacy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDecodeConfigFromTOML(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
tomlConfig := `
|
||||
|
||||
someOtherValue = "foo"
|
||||
|
||||
[privacy]
|
||||
[privacy.disqus]
|
||||
disable = true
|
||||
[privacy.googleAnalytics]
|
||||
disable = true
|
||||
respectDoNotTrack = true
|
||||
anonymizeIP = true
|
||||
useSessionStorage = true
|
||||
[privacy.instagram]
|
||||
disable = true
|
||||
simple = true
|
||||
[privacy.twitter]
|
||||
disable = true
|
||||
enableDNT = true
|
||||
simple = true
|
||||
[privacy.vimeo]
|
||||
disable = true
|
||||
simple = true
|
||||
[privacy.youtube]
|
||||
disable = true
|
||||
privacyEnhanced = true
|
||||
simple = true
|
||||
`
|
||||
cfg, err := config.FromConfigString(tomlConfig, "toml")
|
||||
assert.NoError(err)
|
||||
|
||||
pc, err := DecodeConfig(cfg)
|
||||
assert.NoError(err)
|
||||
assert.NotNil(pc)
|
||||
|
||||
assert.True(pc.Disqus.Disable)
|
||||
assert.True(pc.GoogleAnalytics.Disable)
|
||||
assert.True(pc.GoogleAnalytics.RespectDoNotTrack)
|
||||
assert.True(pc.GoogleAnalytics.AnonymizeIP)
|
||||
assert.True(pc.GoogleAnalytics.UseSessionStorage)
|
||||
assert.True(pc.Instagram.Disable)
|
||||
assert.True(pc.Instagram.Simple)
|
||||
assert.True(pc.Twitter.Disable)
|
||||
assert.True(pc.Twitter.EnableDNT)
|
||||
assert.True(pc.Twitter.Simple)
|
||||
assert.True(pc.Vimeo.Disable)
|
||||
assert.True(pc.Vimeo.Simple)
|
||||
assert.True(pc.YouTube.PrivacyEnhanced)
|
||||
assert.True(pc.YouTube.Disable)
|
||||
}
|
||||
|
||||
func TestDecodeConfigFromTOMLCaseInsensitive(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
tomlConfig := `
|
||||
|
||||
someOtherValue = "foo"
|
||||
|
||||
[Privacy]
|
||||
[Privacy.YouTube]
|
||||
PrivacyENhanced = true
|
||||
`
|
||||
cfg, err := config.FromConfigString(tomlConfig, "toml")
|
||||
assert.NoError(err)
|
||||
|
||||
pc, err := DecodeConfig(cfg)
|
||||
assert.NoError(err)
|
||||
assert.NotNil(pc)
|
||||
assert.True(pc.YouTube.PrivacyEnhanced)
|
||||
}
|
||||
|
||||
func TestDecodeConfigDefault(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
pc, err := DecodeConfig(viper.New())
|
||||
assert.NoError(err)
|
||||
assert.NotNil(pc)
|
||||
assert.False(pc.YouTube.PrivacyEnhanced)
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 services
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
const (
|
||||
servicesConfigKey = "services"
|
||||
|
||||
disqusShortnameKey = "disqusshortname"
|
||||
googleAnalyticsKey = "googleanalytics"
|
||||
)
|
||||
|
||||
// Config is a privacy configuration for all the relevant services in Hugo.
|
||||
type Config struct {
|
||||
Disqus Disqus
|
||||
GoogleAnalytics GoogleAnalytics
|
||||
Instagram Instagram
|
||||
Twitter Twitter
|
||||
}
|
||||
|
||||
// Disqus holds the functional configuration settings related to the Disqus template.
|
||||
type Disqus struct {
|
||||
// A Shortname is the unique identifier assigned to a Disqus site.
|
||||
Shortname string
|
||||
}
|
||||
|
||||
// GoogleAnalytics holds the functional configuration settings related to the Google Analytics template.
|
||||
type GoogleAnalytics struct {
|
||||
// The GA tracking ID.
|
||||
ID string
|
||||
}
|
||||
|
||||
// Instagram holds the functional configuration settings related to the Instagram shortcodes.
|
||||
type Instagram struct {
|
||||
// The Simple variant of the Instagram is decorated with Bootstrap 4 card classes.
|
||||
// This means that if you use Bootstrap 4 or want to provide your own CSS, you want
|
||||
// to disable the inline CSS provided by Hugo.
|
||||
DisableInlineCSS bool
|
||||
}
|
||||
|
||||
// Twitter holds the functional configuration settings related to the Twitter shortcodes.
|
||||
type Twitter struct {
|
||||
// The Simple variant of Twitter is decorated with a basic set of inline styles.
|
||||
// This means that if you want to provide your own CSS, you want
|
||||
// to disable the inline CSS provided by Hugo.
|
||||
DisableInlineCSS bool
|
||||
}
|
||||
|
||||
// DecodeConfig creates a services Config from a given Hugo configuration.
|
||||
func DecodeConfig(cfg config.Provider) (c Config, err error) {
|
||||
m := cfg.GetStringMap(servicesConfigKey)
|
||||
|
||||
err = mapstructure.WeakDecode(m, &c)
|
||||
|
||||
// Keep backwards compatibility.
|
||||
if c.GoogleAnalytics.ID == "" {
|
||||
// Try the global config
|
||||
c.GoogleAnalytics.ID = cfg.GetString(googleAnalyticsKey)
|
||||
}
|
||||
if c.Disqus.Shortname == "" {
|
||||
c.Disqus.Shortname = cfg.GetString(disqusShortnameKey)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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 services
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDecodeConfigFromTOML(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
tomlConfig := `
|
||||
|
||||
someOtherValue = "foo"
|
||||
|
||||
[services]
|
||||
[services.disqus]
|
||||
shortname = "DS"
|
||||
[services.googleAnalytics]
|
||||
id = "ga_id"
|
||||
[services.instagram]
|
||||
disableInlineCSS = true
|
||||
[services.twitter]
|
||||
disableInlineCSS = true
|
||||
`
|
||||
cfg, err := config.FromConfigString(tomlConfig, "toml")
|
||||
assert.NoError(err)
|
||||
|
||||
config, err := DecodeConfig(cfg)
|
||||
assert.NoError(err)
|
||||
assert.NotNil(config)
|
||||
|
||||
assert.Equal("DS", config.Disqus.Shortname)
|
||||
assert.Equal("ga_id", config.GoogleAnalytics.ID)
|
||||
|
||||
assert.True(config.Instagram.DisableInlineCSS)
|
||||
}
|
||||
|
||||
// Support old root-level GA settings etc.
|
||||
func TestUseSettingsFromRootIfSet(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
cfg := viper.New()
|
||||
cfg.Set("disqusShortname", "root_short")
|
||||
cfg.Set("googleAnalytics", "ga_root")
|
||||
|
||||
config, err := DecodeConfig(cfg)
|
||||
assert.NoError(err)
|
||||
assert.NotNil(config)
|
||||
|
||||
assert.Equal("root_short", config.Disqus.Shortname)
|
||||
assert.Equal("ga_root", config.GoogleAnalytics.ID)
|
||||
|
||||
}
|
||||
+232
-70
@@ -16,70 +16,77 @@ package create
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/spf13/afero"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
// NewContent creates a new content file in the content directory based upon the
|
||||
// given kind, which is used to lookup an archetype.
|
||||
func NewContent(
|
||||
ps *helpers.PathSpec,
|
||||
siteFactory func(filename string, siteUsed bool) (*hugolib.Site, error), kind, targetPath string) error {
|
||||
sites *hugolib.HugoSites, kind, targetPath string) error {
|
||||
targetPath = filepath.Clean(targetPath)
|
||||
ext := helpers.Ext(targetPath)
|
||||
ps := sites.PathSpec
|
||||
archetypeFs := ps.BaseFs.SourceFilesystems.Archetypes.Fs
|
||||
sourceFs := ps.Fs.Source
|
||||
|
||||
jww.INFO.Printf("attempting to create %q of %q of ext %q", targetPath, kind, ext)
|
||||
|
||||
archetypeFilename := findArchetype(ps, kind, ext)
|
||||
archetypeFilename, isDir := findArchetype(ps, kind, ext)
|
||||
contentPath, s := resolveContentPath(sites, sourceFs, targetPath)
|
||||
|
||||
if isDir {
|
||||
|
||||
langFs := hugofs.NewLanguageFs(s.Language.Lang, sites.LanguageSet(), archetypeFs)
|
||||
|
||||
cm, err := mapArcheTypeDir(ps, langFs, archetypeFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cm.siteUsed {
|
||||
if err := sites.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
name := filepath.Base(targetPath)
|
||||
return newContentFromDir(archetypeFilename, sites, archetypeFs, sourceFs, cm, name, contentPath)
|
||||
}
|
||||
|
||||
// Building the sites can be expensive, so only do it if really needed.
|
||||
siteUsed := false
|
||||
|
||||
if archetypeFilename != "" {
|
||||
f, err := ps.Fs.Source.Open(archetypeFilename)
|
||||
var err error
|
||||
siteUsed, err = usesSiteVar(archetypeFs, archetypeFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
|
||||
if helpers.ReaderContains(f, []byte(".Site")) {
|
||||
siteUsed = true
|
||||
if siteUsed {
|
||||
if err := sites.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s, err := siteFactory(targetPath, siteUsed)
|
||||
content, err := executeArcheTypeAsTemplate(s, "", kind, targetPath, archetypeFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var content []byte
|
||||
|
||||
content, err = executeArcheTypeAsTemplate(s, kind, targetPath, archetypeFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The site may have multiple content dirs, and we currently do not know which contentDir the
|
||||
// user wants to create this content in. We should improve on this, but we start by testing if the
|
||||
// provided path points to an existing dir. If so, use it as is.
|
||||
var contentPath string
|
||||
var exists bool
|
||||
targetDir := filepath.Dir(targetPath)
|
||||
|
||||
if targetDir != "" && targetDir != "." {
|
||||
exists, _ = helpers.Exists(targetDir, ps.Fs.Source)
|
||||
}
|
||||
|
||||
if exists {
|
||||
contentPath = targetPath
|
||||
} else {
|
||||
contentPath = s.PathSpec.AbsPathify(filepath.Join(s.Cfg.GetString("contentDir"), targetPath))
|
||||
}
|
||||
|
||||
if err := helpers.SafeWriteToDisk(contentPath, bytes.NewReader(content), s.Fs.Source); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -101,44 +108,199 @@ func NewContent(
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindArchetype takes a given kind/archetype of content and returns an output
|
||||
// path for that archetype. If no archetype is found, an empty string is
|
||||
// returned.
|
||||
func findArchetype(ps *helpers.PathSpec, kind, ext string) (outpath string) {
|
||||
search := []string{ps.AbsPathify(ps.Cfg.GetString("archetypeDir"))}
|
||||
|
||||
if ps.Cfg.GetString("theme") != "" {
|
||||
themeDir := filepath.Join(ps.AbsPathify(ps.Cfg.GetString("themesDir")+"/"+ps.Cfg.GetString("theme")), "/archetypes/")
|
||||
if _, err := ps.Fs.Source.Stat(themeDir); os.IsNotExist(err) {
|
||||
jww.ERROR.Printf("Unable to find archetypes directory for theme %q at %q", ps.Cfg.GetString("theme"), themeDir)
|
||||
} else {
|
||||
search = append(search, themeDir)
|
||||
func targetSite(sites *hugolib.HugoSites, fi *hugofs.LanguageFileInfo) *hugolib.Site {
|
||||
for _, s := range sites.Sites {
|
||||
if fi.Lang() == s.Language.Lang {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
for _, x := range search {
|
||||
// If the new content isn't in a subdirectory, kind == "".
|
||||
// Therefore it should be excluded otherwise `is a directory`
|
||||
// error will occur. github.com/gohugoio/hugo/issues/411
|
||||
var pathsToCheck = []string{"default"}
|
||||
|
||||
if ext != "" {
|
||||
if kind != "" {
|
||||
pathsToCheck = append([]string{kind + ext, "default" + ext}, pathsToCheck...)
|
||||
} else {
|
||||
pathsToCheck = append([]string{"default" + ext}, pathsToCheck...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range pathsToCheck {
|
||||
curpath := filepath.Join(x, p)
|
||||
jww.DEBUG.Println("checking", curpath, "for archetypes")
|
||||
if exists, _ := helpers.Exists(curpath, ps.Fs.Source); exists {
|
||||
jww.INFO.Println("curpath: " + curpath)
|
||||
return curpath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
return sites.Sites[0]
|
||||
}
|
||||
|
||||
func newContentFromDir(
|
||||
archetypeDir string,
|
||||
sites *hugolib.HugoSites,
|
||||
sourceFs, targetFs afero.Fs,
|
||||
cm archetypeMap, name, targetPath string) error {
|
||||
|
||||
for _, f := range cm.otherFiles {
|
||||
filename := f.Filename()
|
||||
// Just copy the file to destination.
|
||||
in, err := sourceFs.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetFilename := filepath.Join(targetPath, strings.TrimPrefix(filename, archetypeDir))
|
||||
|
||||
targetDir := filepath.Dir(targetFilename)
|
||||
if err := targetFs.MkdirAll(targetDir, 0777); err != nil && !os.IsExist(err) {
|
||||
return fmt.Errorf("failed to create target directory for %s: %s", targetDir, err)
|
||||
}
|
||||
|
||||
out, err := targetFs.Create(targetFilename)
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
in.Close()
|
||||
out.Close()
|
||||
}
|
||||
|
||||
for _, f := range cm.contentFiles {
|
||||
filename := f.Filename()
|
||||
s := targetSite(sites, f)
|
||||
targetFilename := filepath.Join(targetPath, strings.TrimPrefix(filename, archetypeDir))
|
||||
|
||||
content, err := executeArcheTypeAsTemplate(s, name, archetypeDir, targetFilename, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := helpers.SafeWriteToDisk(targetFilename, bytes.NewReader(content), targetFs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
jww.FEEDBACK.Println(targetPath, "created")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type archetypeMap struct {
|
||||
// These needs to be parsed and executed as Go templates.
|
||||
contentFiles []*hugofs.LanguageFileInfo
|
||||
// These are just copied to destination.
|
||||
otherFiles []*hugofs.LanguageFileInfo
|
||||
// If the templates needs a fully built site. This can potentially be
|
||||
// expensive, so only do when needed.
|
||||
siteUsed bool
|
||||
}
|
||||
|
||||
func mapArcheTypeDir(
|
||||
ps *helpers.PathSpec,
|
||||
fs afero.Fs,
|
||||
archetypeDir string) (archetypeMap, error) {
|
||||
|
||||
var m archetypeMap
|
||||
|
||||
walkFn := func(filename string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
fil := fi.(*hugofs.LanguageFileInfo)
|
||||
|
||||
if hugolib.IsContentFile(filename) {
|
||||
m.contentFiles = append(m.contentFiles, fil)
|
||||
if !m.siteUsed {
|
||||
m.siteUsed, err = usesSiteVar(fs, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
m.otherFiles = append(m.otherFiles, fil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := helpers.SymbolicWalk(fs, archetypeDir, walkFn); err != nil {
|
||||
return m, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func usesSiteVar(fs afero.Fs, filename string) (bool, error) {
|
||||
f, err := fs.Open(filename)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to open archetype file: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
return helpers.ReaderContains(f, []byte(".Site")), nil
|
||||
}
|
||||
|
||||
// Resolve the target content path.
|
||||
func resolveContentPath(sites *hugolib.HugoSites, fs afero.Fs, targetPath string) (string, *hugolib.Site) {
|
||||
targetDir := filepath.Dir(targetPath)
|
||||
first := sites.Sites[0]
|
||||
|
||||
var (
|
||||
s *hugolib.Site
|
||||
siteContentDir string
|
||||
)
|
||||
|
||||
// Try the filename: my-post.en.md
|
||||
for _, ss := range sites.Sites {
|
||||
if strings.Contains(targetPath, "."+ss.Language.Lang+".") {
|
||||
s = ss
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, ss := range sites.Sites {
|
||||
contentDir := ss.PathSpec.ContentDir
|
||||
if !strings.HasSuffix(contentDir, helpers.FilePathSeparator) {
|
||||
contentDir += helpers.FilePathSeparator
|
||||
}
|
||||
if strings.HasPrefix(targetPath, contentDir) {
|
||||
siteContentDir = ss.PathSpec.ContentDir
|
||||
if s == nil {
|
||||
s = ss
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
s = first
|
||||
}
|
||||
|
||||
if targetDir != "" && targetDir != "." {
|
||||
exists, _ := helpers.Exists(targetDir, fs)
|
||||
|
||||
if exists {
|
||||
return targetPath, s
|
||||
}
|
||||
}
|
||||
|
||||
if siteContentDir != "" {
|
||||
pp := filepath.Join(siteContentDir, strings.TrimPrefix(targetPath, siteContentDir))
|
||||
return s.PathSpec.AbsPathify(pp), s
|
||||
|
||||
} else {
|
||||
return s.PathSpec.AbsPathify(filepath.Join(first.PathSpec.ContentDir, targetPath)), s
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// FindArchetype takes a given kind/archetype of content and returns the path
|
||||
// to the archetype in the archetype filesystem, blank if none found.
|
||||
func findArchetype(ps *helpers.PathSpec, kind, ext string) (outpath string, isDir bool) {
|
||||
fs := ps.BaseFs.Archetypes.Fs
|
||||
|
||||
var pathsToCheck []string
|
||||
|
||||
if kind != "" {
|
||||
pathsToCheck = append(pathsToCheck, kind+ext)
|
||||
}
|
||||
pathsToCheck = append(pathsToCheck, "default"+ext, "default")
|
||||
|
||||
for _, p := range pathsToCheck {
|
||||
fi, err := fs.Stat(p)
|
||||
if err == nil {
|
||||
return p, fi.IsDir()
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ type ArchetypeFileData struct {
|
||||
// used in the archetype template. Also, if this is a multilingual setup,
|
||||
// this site is the site that best matches the target content file, based
|
||||
// on the presence of language code in the filename.
|
||||
Site *hugolib.Site
|
||||
Site *hugolib.SiteInfo
|
||||
|
||||
// Name will in most cases be the same as TranslationBaseName, e.g. "my-post".
|
||||
// But if that value is "index" (bundles), the Name is instead the owning folder.
|
||||
@@ -80,7 +80,7 @@ var (
|
||||
"%}x}", "%}}")
|
||||
)
|
||||
|
||||
func executeArcheTypeAsTemplate(s *hugolib.Site, kind, targetPath, archetypeFilename string) ([]byte, error) {
|
||||
func executeArcheTypeAsTemplate(s *hugolib.Site, name, kind, targetPath, archetypeFilename string) ([]byte, error) {
|
||||
|
||||
var (
|
||||
archetypeContent []byte
|
||||
@@ -88,19 +88,16 @@ func executeArcheTypeAsTemplate(s *hugolib.Site, kind, targetPath, archetypeFile
|
||||
err error
|
||||
)
|
||||
|
||||
ps, err := helpers.NewPathSpec(s.Deps.Fs, s.Deps.Cfg)
|
||||
sp := source.NewSourceSpec(ps, ps.Fs.Source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f := sp.NewFileInfo("", targetPath, false, nil)
|
||||
f := s.SourceSpec.NewFileInfo("", targetPath, false, nil)
|
||||
|
||||
name := f.TranslationBaseName()
|
||||
if name == "" {
|
||||
name = f.TranslationBaseName()
|
||||
|
||||
if name == "index" || name == "_index" {
|
||||
// Page bundles; the directory name will hopefully have a better name.
|
||||
dir := strings.TrimSuffix(f.Dir(), helpers.FilePathSeparator)
|
||||
_, name = filepath.Split(dir)
|
||||
if name == "index" || name == "_index" {
|
||||
// Page bundles; the directory name will hopefully have a better name.
|
||||
dir := strings.TrimSuffix(f.Dir(), helpers.FilePathSeparator)
|
||||
_, name = filepath.Split(dir)
|
||||
}
|
||||
}
|
||||
|
||||
data := ArchetypeFileData{
|
||||
@@ -108,16 +105,16 @@ func executeArcheTypeAsTemplate(s *hugolib.Site, kind, targetPath, archetypeFile
|
||||
Date: time.Now().Format(time.RFC3339),
|
||||
Name: name,
|
||||
File: f,
|
||||
Site: s,
|
||||
Site: &s.Info,
|
||||
}
|
||||
|
||||
if archetypeFilename == "" {
|
||||
// TODO(bep) archetype revive the issue about wrong tpl funcs arg order
|
||||
archetypeTemplate = []byte(ArchetypeTemplateTemplate)
|
||||
} else {
|
||||
archetypeTemplate, err = afero.ReadFile(s.Fs.Source, archetypeFilename)
|
||||
archetypeTemplate, err = afero.ReadFile(s.BaseFs.Archetypes.Fs, archetypeFilename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read archetype file %q: %s", archetypeFilename, err)
|
||||
return nil, fmt.Errorf("failed to read archetype file %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -133,7 +130,7 @@ func executeArcheTypeAsTemplate(s *hugolib.Site, kind, targetPath, archetypeFile
|
||||
return nil, fmt.Errorf("Failed to parse archetype file %q: %s", archetypeFilename, err)
|
||||
}
|
||||
|
||||
templ := templateHandler.Lookup(templateName)
|
||||
templ, _ := templateHandler.Lookup(templateName)
|
||||
|
||||
var buff bytes.Buffer
|
||||
if err := templ.Execute(&buff, data); err != nil {
|
||||
|
||||
+91
-29
@@ -35,8 +35,7 @@ import (
|
||||
)
|
||||
|
||||
func TestNewContent(t *testing.T) {
|
||||
v := viper.New()
|
||||
initViper(v)
|
||||
assert := require.New(t)
|
||||
|
||||
cases := []struct {
|
||||
kind string
|
||||
@@ -49,6 +48,14 @@ func TestNewContent(t *testing.T) {
|
||||
{"stump", "stump/sample-2.md", []string{`title: "Sample 2"`}}, // no archetype file
|
||||
{"", "sample-3.md", []string{`title: "Sample 3"`}}, // no archetype
|
||||
{"product", "product/sample-4.md", []string{`title = "SAMPLE-4"`}}, // empty archetype front matter
|
||||
{"lang", "post/lang-1.md", []string{`Site Lang: en|Name: Lang 1|i18n: Hugo Rocks!`}},
|
||||
{"lang", "post/lang-2.en.md", []string{`Site Lang: en|Name: Lang 2|i18n: Hugo Rocks!`}},
|
||||
{"lang", "post/lang-3.nn.md", []string{`Site Lang: nn|Name: Lang 3|i18n: Hugo Rokkar!`}},
|
||||
{"lang", "content_nn/post/lang-4.md", []string{`Site Lang: nn|Name: Lang 4|i18n: Hugo Rokkar!`}},
|
||||
{"lang", "content_nn/post/lang-5.en.md", []string{`Site Lang: en|Name: Lang 5|i18n: Hugo Rocks!`}},
|
||||
{"lang", "post/my-bundle/index.md", []string{`Site Lang: en|Name: My Bundle|i18n: Hugo Rocks!`}},
|
||||
{"lang", "post/my-bundle/index.en.md", []string{`Site Lang: en|Name: My Bundle|i18n: Hugo Rocks!`}},
|
||||
{"lang", "post/my-bundle/index.nn.md", []string{`Site Lang: nn|Name: My Bundle|i18n: Hugo Rokkar!`}},
|
||||
{"shortcodes", "shortcodes/go.md", []string{
|
||||
`title = "GO"`,
|
||||
"{{< myshortcode >}}",
|
||||
@@ -56,23 +63,20 @@ func TestNewContent(t *testing.T) {
|
||||
"{{</* comment */>}}\n{{%/* comment */%}}"}}, // shortcodes
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
cfg, fs := newTestCfg()
|
||||
ps, err := helpers.NewPathSpec(fs, cfg)
|
||||
require.NoError(t, err)
|
||||
for i, c := range cases {
|
||||
cfg, fs := newTestCfg(assert)
|
||||
assert.NoError(initFs(fs))
|
||||
h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, initFs(fs))
|
||||
assert.NoError(err)
|
||||
|
||||
siteFactory := func(filename string, siteUsed bool) (*hugolib.Site, error) {
|
||||
return h.Sites[0], nil
|
||||
assert.NoError(create.NewContent(h, c.kind, c.path))
|
||||
|
||||
fname := filepath.FromSlash(c.path)
|
||||
if !strings.HasPrefix(fname, "content") {
|
||||
fname = filepath.Join("content", fname)
|
||||
}
|
||||
|
||||
require.NoError(t, create.NewContent(ps, siteFactory, c.kind, c.path))
|
||||
|
||||
fname := filepath.Join("content", filepath.FromSlash(c.path))
|
||||
content := readFileFromFs(t, fs.Source, fname)
|
||||
for i, v := range c.expected {
|
||||
for _, v := range c.expected {
|
||||
found := strings.Contains(content, v)
|
||||
if !found {
|
||||
t.Fatalf("[%d] %q missing from output:\n%q", i, v, content)
|
||||
@@ -81,14 +85,44 @@ func TestNewContent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func initViper(v *viper.Viper) {
|
||||
v.Set("metaDataFormat", "toml")
|
||||
v.Set("archetypeDir", "archetypes")
|
||||
v.Set("contentDir", "content")
|
||||
v.Set("themesDir", "themes")
|
||||
v.Set("layoutDir", "layouts")
|
||||
v.Set("i18nDir", "i18n")
|
||||
v.Set("theme", "sample")
|
||||
func TestNewContentFromDir(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
cfg, fs := newTestCfg(assert)
|
||||
assert.NoError(initFs(fs))
|
||||
|
||||
archetypeDir := filepath.Join("archetypes", "my-bundle")
|
||||
assert.NoError(fs.Source.Mkdir(archetypeDir, 0755))
|
||||
|
||||
contentFile := `
|
||||
File: %s
|
||||
Site Lang: {{ .Site.Language.Lang }}
|
||||
Name: {{ replace .Name "-" " " | title }}
|
||||
i18n: {{ T "hugo" }}
|
||||
`
|
||||
|
||||
assert.NoError(afero.WriteFile(fs.Source, filepath.Join(archetypeDir, "index.md"), []byte(fmt.Sprintf(contentFile, "index.md")), 0755))
|
||||
assert.NoError(afero.WriteFile(fs.Source, filepath.Join(archetypeDir, "index.nn.md"), []byte(fmt.Sprintf(contentFile, "index.nn.md")), 0755))
|
||||
|
||||
assert.NoError(afero.WriteFile(fs.Source, filepath.Join(archetypeDir, "pages", "bio.md"), []byte(fmt.Sprintf(contentFile, "bio.md")), 0755))
|
||||
assert.NoError(afero.WriteFile(fs.Source, filepath.Join(archetypeDir, "resources", "hugo1.json"), []byte(`hugo1: {{ printf "no template handling in here" }}`), 0755))
|
||||
assert.NoError(afero.WriteFile(fs.Source, filepath.Join(archetypeDir, "resources", "hugo2.xml"), []byte(`hugo2: {{ printf "no template handling in here" }}`), 0755))
|
||||
|
||||
h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
|
||||
assert.NoError(err)
|
||||
assert.Equal(2, len(h.Sites))
|
||||
|
||||
assert.NoError(create.NewContent(h, "my-bundle", "post/my-post"))
|
||||
|
||||
assertContains(assert, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo1.json")), `hugo1: {{ printf "no template handling in here" }}`)
|
||||
assertContains(assert, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo2.xml")), `hugo2: {{ printf "no template handling in here" }}`)
|
||||
|
||||
// Content files should get the correct site context.
|
||||
// TODO(bep) archetype check i18n
|
||||
assertContains(assert, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/index.md")), `File: index.md`, `Site Lang: en`, `Name: My Post`, `i18n: Hugo Rocks!`)
|
||||
assertContains(assert, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/index.nn.md")), `File: index.nn.md`, `Site Lang: nn`, `Name: My Post`, `i18n: Hugo Rokkar!`)
|
||||
|
||||
assertContains(assert, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/pages/bio.md")), `File: bio.md`, `Site Lang: en`, `Name: My Post`)
|
||||
|
||||
}
|
||||
|
||||
func initFs(fs *hugofs.Fs) error {
|
||||
@@ -131,6 +165,10 @@ title = "{{ .BaseFileName | upper }}"
|
||||
path: filepath.Join("archetypes", "emptydate.md"),
|
||||
content: "+++\ndate =\"\"\ntitle = \"Empty Date Arch title\"\ntest = \"test1\"\n+++\n",
|
||||
},
|
||||
{
|
||||
path: filepath.Join("archetypes", "lang.md"),
|
||||
content: `Site Lang: {{ .Site.Language.Lang }}|Name: {{ replace .Name "-" " " | title }}|i18n: {{ T "hugo" }}`,
|
||||
},
|
||||
// #3623x
|
||||
{
|
||||
path: filepath.Join("archetypes", "shortcodes.md"),
|
||||
@@ -165,6 +203,12 @@ Some text.
|
||||
return nil
|
||||
}
|
||||
|
||||
func assertContains(assert *require.Assertions, v interface{}, matches ...string) {
|
||||
for _, m := range matches {
|
||||
assert.Contains(v, m)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bep) extract common testing package with this and some others
|
||||
func readFileFromFs(t *testing.T, fs afero.Fs, filename string) string {
|
||||
filename = filepath.FromSlash(filename)
|
||||
@@ -184,15 +228,33 @@ func readFileFromFs(t *testing.T, fs afero.Fs, filename string) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func newTestCfg() (*viper.Viper, *hugofs.Fs) {
|
||||
func newTestCfg(assert *require.Assertions) (*viper.Viper, *hugofs.Fs) {
|
||||
|
||||
v := viper.New()
|
||||
fs := hugofs.NewMem(v)
|
||||
cfg := `
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
languageName = "English"
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
languageName = "Nynorsk"
|
||||
contentDir = "content_nn"
|
||||
|
||||
v.SetFs(fs.Source)
|
||||
`
|
||||
|
||||
initViper(v)
|
||||
mm := afero.NewMemMapFs()
|
||||
|
||||
return v, fs
|
||||
assert.NoError(afero.WriteFile(mm, filepath.Join("i18n", "en.toml"), []byte(`[hugo]
|
||||
other = "Hugo Rocks!"`), 0755))
|
||||
assert.NoError(afero.WriteFile(mm, filepath.Join("i18n", "nn.toml"), []byte(`[hugo]
|
||||
other = "Hugo Rokkar!"`), 0755))
|
||||
|
||||
assert.NoError(afero.WriteFile(mm, "config.toml", []byte(cfg), 0755))
|
||||
|
||||
v, _, err := hugolib.LoadConfig(hugolib.ConfigSourceDescriptor{Fs: mm, Filename: "config.toml"})
|
||||
assert.NoError(err)
|
||||
|
||||
return v, hugofs.NewFrom(mm, v)
|
||||
|
||||
}
|
||||
|
||||
Vendored
+86
-8
@@ -1,15 +1,19 @@
|
||||
package deps
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/metrics"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/resource"
|
||||
"github.com/gohugoio/hugo/source"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
@@ -19,12 +23,19 @@ import (
|
||||
// There will be normally only one instance of deps in play
|
||||
// at a given time, i.e. one per Site built.
|
||||
type Deps struct {
|
||||
|
||||
// The logger to use.
|
||||
Log *jww.Notepad `json:"-"`
|
||||
|
||||
// Used to log errors that may repeat itself many times.
|
||||
DistinctErrorLog *helpers.DistinctLogger
|
||||
|
||||
// The templates to use. This will usually implement the full tpl.TemplateHandler.
|
||||
Tmpl tpl.TemplateFinder `json:"-"`
|
||||
|
||||
// We use this to parse and execute ad-hoc text templates.
|
||||
TextTmpl tpl.TemplateParseFinder `json:"-"`
|
||||
|
||||
// The file systems to use.
|
||||
Fs *hugofs.Fs `json:"-"`
|
||||
|
||||
@@ -37,13 +48,16 @@ type Deps struct {
|
||||
// The SourceSpec to use
|
||||
SourceSpec *source.SourceSpec `json:"-"`
|
||||
|
||||
// The Resource Spec to use
|
||||
ResourceSpec *resource.Spec
|
||||
|
||||
// The configuration to use
|
||||
Cfg config.Provider `json:"-"`
|
||||
|
||||
// The translation func to use
|
||||
Translate func(translationID string, args ...interface{}) string `json:"-"`
|
||||
|
||||
Language *helpers.Language
|
||||
Language *langs.Language
|
||||
|
||||
// All the output formats available for the current site.
|
||||
OutputFormatsConfig output.Formats
|
||||
@@ -54,6 +68,36 @@ type Deps struct {
|
||||
translationProvider ResourceProvider
|
||||
|
||||
Metrics metrics.Provider
|
||||
|
||||
// Timeout is configurable in site config.
|
||||
Timeout time.Duration
|
||||
|
||||
// BuildStartListeners will be notified before a build starts.
|
||||
BuildStartListeners *Listeners
|
||||
}
|
||||
|
||||
// Listeners represents an event listener.
|
||||
type Listeners struct {
|
||||
sync.Mutex
|
||||
|
||||
// A list of funcs to be notified about an event.
|
||||
listeners []func()
|
||||
}
|
||||
|
||||
// Add adds a function to a Listeners instance.
|
||||
func (b *Listeners) Add(f func()) {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
b.listeners = append(b.listeners, f)
|
||||
}
|
||||
|
||||
// Notify executes all listener functions.
|
||||
func (b *Listeners) Notify() {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
for _, notify := range b.listeners {
|
||||
notify()
|
||||
}
|
||||
}
|
||||
|
||||
// ResourceProvider is used to create and refresh, and clone resources needed.
|
||||
@@ -107,7 +151,7 @@ func New(cfg DepsCfg) (*Deps, error) {
|
||||
}
|
||||
|
||||
if logger == nil {
|
||||
logger = jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
|
||||
logger = loggers.NewErrorLogger()
|
||||
}
|
||||
|
||||
if fs == nil {
|
||||
@@ -121,6 +165,11 @@ func New(cfg DepsCfg) (*Deps, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceSpec, err := resource.NewSpec(ps, logger, cfg.OutputFormats, cfg.MediaTypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contentSpec, err := helpers.NewContentSpec(cfg.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -128,17 +177,28 @@ func New(cfg DepsCfg) (*Deps, error) {
|
||||
|
||||
sp := source.NewSourceSpec(ps, fs.Source)
|
||||
|
||||
timeoutms := cfg.Language.GetInt("timeout")
|
||||
if timeoutms <= 0 {
|
||||
timeoutms = 3000
|
||||
}
|
||||
|
||||
distinctErrorLogger := helpers.NewDistinctLogger(logger.ERROR)
|
||||
|
||||
d := &Deps{
|
||||
Fs: fs,
|
||||
Log: logger,
|
||||
DistinctErrorLog: distinctErrorLogger,
|
||||
templateProvider: cfg.TemplateProvider,
|
||||
translationProvider: cfg.TranslationProvider,
|
||||
WithTemplate: cfg.WithTemplate,
|
||||
PathSpec: ps,
|
||||
ContentSpec: contentSpec,
|
||||
SourceSpec: sp,
|
||||
ResourceSpec: resourceSpec,
|
||||
Cfg: cfg.Language,
|
||||
Language: cfg.Language,
|
||||
BuildStartListeners: &Listeners{},
|
||||
Timeout: time.Duration(timeoutms) * time.Millisecond,
|
||||
}
|
||||
|
||||
if cfg.Cfg.GetBool("templateMetrics") {
|
||||
@@ -150,10 +210,11 @@ func New(cfg DepsCfg) (*Deps, error) {
|
||||
|
||||
// ForLanguage creates a copy of the Deps with the language dependent
|
||||
// parts switched out.
|
||||
func (d Deps) ForLanguage(l *helpers.Language) (*Deps, error) {
|
||||
func (d Deps) ForLanguage(cfg DepsCfg) (*Deps, error) {
|
||||
l := cfg.Language
|
||||
var err error
|
||||
|
||||
d.PathSpec, err = helpers.NewPathSpec(d.Fs, l)
|
||||
d.PathSpec, err = helpers.NewPathSpecWithBaseBaseFsProvided(d.Fs, l, d.BaseFs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -163,6 +224,15 @@ func (d Deps) ForLanguage(l *helpers.Language) (*Deps, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The resource cache is global so reuse.
|
||||
// TODO(bep) clean up these inits.
|
||||
resourceCache := d.ResourceSpec.ResourceCache
|
||||
d.ResourceSpec, err = resource.NewSpec(d.PathSpec, d.Log, cfg.OutputFormats, cfg.MediaTypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.ResourceSpec.ResourceCache = resourceCache
|
||||
|
||||
d.Cfg = l
|
||||
d.Language = l
|
||||
|
||||
@@ -174,6 +244,8 @@ func (d Deps) ForLanguage(l *helpers.Language) (*Deps, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.BuildStartListeners = &Listeners{}
|
||||
|
||||
return &d, nil
|
||||
|
||||
}
|
||||
@@ -190,11 +262,17 @@ type DepsCfg struct {
|
||||
Fs *hugofs.Fs
|
||||
|
||||
// The language to use.
|
||||
Language *helpers.Language
|
||||
Language *langs.Language
|
||||
|
||||
// The configuration to use.
|
||||
Cfg config.Provider
|
||||
|
||||
// The media types configured.
|
||||
MediaTypes media.Types
|
||||
|
||||
// The output formats configured.
|
||||
OutputFormats output.Formats
|
||||
|
||||
// Template handling.
|
||||
TemplateProvider ResourceProvider
|
||||
WithTemplate func(templ tpl.TemplateHandler) error
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Hugo Docs
|
||||
|
||||
Documentation site for [Hugo](https://github.com/gohugoio/hugo), the very fast and flexible static site generator built with love in GoLang.
|
||||
Documentation site for [Hugo](https://github.com/gohugoio/hugo), the very fast and flexible static site generator built with love in Go.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
+142
-3
@@ -34,7 +34,6 @@ section = [ "HTML", "RSS"]
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."text/netlify"]
|
||||
suffix = ""
|
||||
delimiter = ""
|
||||
|
||||
[outputFormats]
|
||||
@@ -70,7 +69,7 @@ twitter = "GoHugoIO"
|
||||
[params]
|
||||
description = "The world’s fastest framework for building websites"
|
||||
## Used for views in rendered HTML (i.e., rather than using the .Hugo variable)
|
||||
release = "0.39"
|
||||
release = "0.49"
|
||||
## Setting this to true will add a "noindex" to *EVERY* page on the site
|
||||
removefromexternalsearch = false
|
||||
## Gh repo for site footer (include trailing slash)
|
||||
@@ -167,7 +166,11 @@ anchor = "smart"
|
||||
weight = 35
|
||||
identifier = "variables"
|
||||
url = "/variables/"
|
||||
|
||||
[[menu.docs]]
|
||||
name = "Hugo Pipes"
|
||||
weight = 36
|
||||
identifier = "pipes"
|
||||
url = "/hugo-pipes/"
|
||||
[[menu.docs]]
|
||||
name = "CLI"
|
||||
weight = 40
|
||||
@@ -271,3 +274,139 @@ anchor = "smart"
|
||||
identifier = "github"
|
||||
post = "external"
|
||||
url = "https://github.com/gohugoio/hugo"
|
||||
|
||||
### LANGUAGES ###
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
contentDir = "content/en"
|
||||
languageName = "English"
|
||||
weight = 1
|
||||
[languages.zh]
|
||||
contentDir = "content/zh"
|
||||
languageName = "中文"
|
||||
weight = 2
|
||||
|
||||
|
||||
### LANGUAGE-SPECIFIC MENUS ###
|
||||
|
||||
# Chinese menus
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "关于 Hugo"
|
||||
weight = 1
|
||||
identifier = "about"
|
||||
url = "/zh/about/"
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "入门"
|
||||
weight = 5
|
||||
identifier = "getting-started"
|
||||
url = "/zh/getting-started/"
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "主题"
|
||||
weight = 15
|
||||
identifier = "themes"
|
||||
post = "break"
|
||||
url = "/zh/themes/"
|
||||
|
||||
# Core languages.zh.menus
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "内容管理"
|
||||
weight = 20
|
||||
identifier = "content-management"
|
||||
post = "expanded"
|
||||
url = "/zh/content-management/"
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "模板"
|
||||
weight = 25
|
||||
identifier = "templates"
|
||||
url = "/zh/templates/"
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "函数"
|
||||
weight = 30
|
||||
identifier = "functions"
|
||||
url = "/zh/functions/"
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "变量"
|
||||
weight = 35
|
||||
identifier = "variables"
|
||||
url = "/zh/variables/"
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "CLI"
|
||||
weight = 40
|
||||
post = "break"
|
||||
identifier = "commands"
|
||||
url = "/commands/"
|
||||
|
||||
# LOW LEVEL ITEMS
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "故障排除"
|
||||
weight = 60
|
||||
identifier = "troubleshooting"
|
||||
url = "/zh/troubleshooting/"
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "工具"
|
||||
weight = 70
|
||||
identifier = "tools"
|
||||
url = "/zh/tools/"
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "托管与部署"
|
||||
weight = 80
|
||||
identifier = "hosting-and-deployment"
|
||||
url = "/zh/hosting-and-deployment/"
|
||||
|
||||
[[languages.zh.menu.docs]]
|
||||
name = "贡献"
|
||||
weight = 100
|
||||
post = "break"
|
||||
identifier = "contribute"
|
||||
url = "/zh/contribute/"
|
||||
|
||||
[[languages.zh.menu.global]]
|
||||
name = "新闻"
|
||||
weight = 1
|
||||
identifier = "news"
|
||||
url = "/zh/news/"
|
||||
|
||||
[[languages.zh.menu.global]]
|
||||
name = "文档"
|
||||
weight = 5
|
||||
identifier = "docs"
|
||||
url = "/zh/documentation/"
|
||||
|
||||
[[languages.zh.menu.global]]
|
||||
name = "主题"
|
||||
weight = 10
|
||||
identifier = "themes"
|
||||
url = "https://themes.gohugo.io/"
|
||||
|
||||
[[languages.zh.menu.global]]
|
||||
name = "作品展示"
|
||||
weight = 20
|
||||
identifier = "showcase"
|
||||
url = "/zh/showcase/"
|
||||
|
||||
# Anything with a weight > 100 gets an external icon
|
||||
[[languages.zh.menu.global]]
|
||||
name = "社区"
|
||||
weight = 150
|
||||
icon = true
|
||||
identifier = "community"
|
||||
post = "external"
|
||||
url = "https://discourse.gohugo.io/"
|
||||
|
||||
[[languages.zh.menu.global]]
|
||||
name = "GitHub"
|
||||
weight = 200
|
||||
identifier = "github"
|
||||
post = "external"
|
||||
url = "https://github.com/gohugoio/hugo"
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo"
|
||||
slug: hugo
|
||||
url: /commands/hugo/
|
||||
---
|
||||
## hugo
|
||||
|
||||
hugo builds your site
|
||||
|
||||
### Synopsis
|
||||
|
||||
hugo is the main command, used to build your Hugo site.
|
||||
|
||||
Hugo is a Fast and Flexible Static Site Generator
|
||||
built with love by spf13 and friends in Go.
|
||||
|
||||
Complete documentation is available at http://gohugo.io/.
|
||||
|
||||
```
|
||||
hugo [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. http://spf13.com/
|
||||
-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/
|
||||
--canonifyURLs (deprecated) if true, all relative URLs will be canonicalized using baseURL
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
--config string config file (default is path/config.yaml|json|toml)
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--debug debug output
|
||||
-d, --destination string filesystem path to write files to
|
||||
--disableKinds stringSlice disable different kind of pages (home, RSS etc.)
|
||||
--enableGitInfo add Git revision, date and author info to the pages
|
||||
--forceSyncStatic copy all files when static is changed.
|
||||
--gc enable to run some cleanup tasks (remove unused cache files) after the build
|
||||
-h, --help help for hugo
|
||||
--i18n-warnings print missing translations
|
||||
--ignoreCache ignores the cache directory
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--noChmod don't sync permission mode of files
|
||||
--noTimes don't sync modification time of files
|
||||
--pluralizeListTitles (deprecated) pluralize titles in lists using inflect (default true)
|
||||
--preserveTaxonomyNames (deprecated) preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")
|
||||
--quiet build in quiet mode
|
||||
--renderToMemory render to memory (only useful for benchmark testing)
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--stepAnalysis display memory and timing of different steps of the program
|
||||
--templateMetrics display metrics about template executions
|
||||
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
|
||||
-t, --theme string theme to use (located in /themes/THEMENAME/)
|
||||
--themesDir string filesystem path to themes directory
|
||||
--uglyURLs (deprecated) if true, use /filename.html instead of /filename/
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
-w, --watch watch filesystem for changes and recreate as needed
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo benchmark](/commands/hugo_benchmark/) - Benchmark Hugo by building a site a number of times.
|
||||
* [hugo check](/commands/hugo_check/) - Contains some verification checks
|
||||
* [hugo config](/commands/hugo_config/) - Print the site configuration
|
||||
* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
|
||||
* [hugo env](/commands/hugo_env/) - Print Hugo version and environment info
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
* [hugo import](/commands/hugo_import/) - Import your site from others.
|
||||
* [hugo list](/commands/hugo_list/) - Listing out various types of content
|
||||
* [hugo new](/commands/hugo_new/) - Create new content for your site
|
||||
* [hugo server](/commands/hugo_server/) - A high performance webserver
|
||||
* [hugo version](/commands/hugo_version/) - Print the version number of Hugo
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo benchmark"
|
||||
slug: hugo_benchmark
|
||||
url: /commands/hugo_benchmark/
|
||||
---
|
||||
## hugo benchmark
|
||||
|
||||
Benchmark Hugo by building a site a number of times.
|
||||
|
||||
### Synopsis
|
||||
|
||||
Hugo can build a site many times over and analyze the running process
|
||||
creating a benchmark.
|
||||
|
||||
```
|
||||
hugo benchmark [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. http://spf13.com/
|
||||
-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/
|
||||
--canonifyURLs (deprecated) if true, all relative URLs will be canonicalized using baseURL
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-n, --count int number of times to build the site (default 13)
|
||||
--cpuprofile string path/filename for the CPU profile file
|
||||
-d, --destination string filesystem path to write files to
|
||||
--disableKinds stringSlice disable different kind of pages (home, RSS etc.)
|
||||
--enableGitInfo add Git revision, date and author info to the pages
|
||||
--forceSyncStatic copy all files when static is changed.
|
||||
--gc enable to run some cleanup tasks (remove unused cache files) after the build
|
||||
-h, --help help for benchmark
|
||||
--i18n-warnings print missing translations
|
||||
--ignoreCache ignores the cache directory
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--memprofile string path/filename for the memory profile file
|
||||
--noChmod don't sync permission mode of files
|
||||
--noTimes don't sync modification time of files
|
||||
--pluralizeListTitles (deprecated) pluralize titles in lists using inflect (default true)
|
||||
--preserveTaxonomyNames (deprecated) preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")
|
||||
--renderToMemory render to memory (only useful for benchmark testing)
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--stepAnalysis display memory and timing of different steps of the program
|
||||
--templateMetrics display metrics about template executions
|
||||
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
|
||||
-t, --theme string theme to use (located in /themes/THEMENAME/)
|
||||
--themesDir string filesystem path to themes directory
|
||||
--uglyURLs (deprecated) if true, use /filename.html instead of /filename/
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string config file (default is path/config.yaml|json|toml)
|
||||
--debug debug output
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--quiet build in quiet mode
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
@@ -1,91 +0,0 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo server"
|
||||
slug: hugo_server
|
||||
url: /commands/hugo_server/
|
||||
---
|
||||
## hugo server
|
||||
|
||||
A high performance webserver
|
||||
|
||||
### Synopsis
|
||||
|
||||
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.
|
||||
|
||||
By default hugo will also watch your files for any changes you make and
|
||||
automatically rebuild the site. It will then live reload any open browser pages
|
||||
and push the latest content to them. As most Hugo sites are built in a fraction
|
||||
of a second, you will be able to save and see your changes nearly instantly.
|
||||
|
||||
```
|
||||
hugo server [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--appendPort append port to baseURL (default true)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. http://spf13.com/
|
||||
--bind string interface to which the server will bind (default "127.0.0.1")
|
||||
-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/
|
||||
--canonifyURLs (deprecated) if true, all relative URLs will be canonicalized using baseURL
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-d, --destination string filesystem path to write files to
|
||||
--disableFastRender enables full re-renders on changes
|
||||
--disableKinds stringSlice disable different kind of pages (home, RSS etc.)
|
||||
--disableLiveReload watch without enabling live browser reload on rebuild
|
||||
--enableGitInfo add Git revision, date and author info to the pages
|
||||
--forceSyncStatic copy all files when static is changed.
|
||||
--gc enable to run some cleanup tasks (remove unused cache files) after the build
|
||||
-h, --help help for server
|
||||
--i18n-warnings print missing translations
|
||||
--ignoreCache ignores the cache directory
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--liveReloadPort int port for live reloading (i.e. 443 in HTTPS proxy situations) (default -1)
|
||||
--meminterval string interval to poll memory usage (requires --memstats), valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". (default "100ms")
|
||||
--memstats string log memory usage to this file
|
||||
--navigateToChanged navigate to changed content file on live browser reload
|
||||
--noChmod don't sync permission mode of files
|
||||
--noHTTPCache prevent HTTP caching
|
||||
--noTimes don't sync modification time of files
|
||||
--pluralizeListTitles (deprecated) pluralize titles in lists using inflect (default true)
|
||||
-p, --port int port on which the server will listen (default 1313)
|
||||
--preserveTaxonomyNames (deprecated) preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")
|
||||
--renderToDisk render to Destination path (default is render to memory & serve from there)
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--stepAnalysis display memory and timing of different steps of the program
|
||||
--templateMetrics display metrics about template executions
|
||||
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
|
||||
-t, --theme string theme to use (located in /themes/THEMENAME/)
|
||||
--themesDir string filesystem path to themes directory
|
||||
--uglyURLs (deprecated) if true, use /filename.html instead of /filename/
|
||||
-w, --watch watch filesystem for changes and recreate as needed (default true)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string config file (default is path/config.yaml|json|toml)
|
||||
--debug debug output
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--quiet build in quiet mode
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
@@ -1,130 +0,0 @@
|
||||
---
|
||||
title: Links and Cross References
|
||||
description: Hugo makes it easy to link documents together.
|
||||
date: 2017-02-01
|
||||
publishdate: 2017-02-01
|
||||
lastmod: 2017-03-31
|
||||
categories: [content management]
|
||||
keywords: ["cross references","references", "anchors", "urls"]
|
||||
menu:
|
||||
docs:
|
||||
parent: "content-management"
|
||||
weight: 100
|
||||
weight: 100 #rem
|
||||
aliases: [/extras/crossreferences/]
|
||||
toc: true
|
||||
---
|
||||
|
||||
|
||||
The `ref` and `relref` shortcodes link documents together, both of which are [built-in Hugo shortcodes][]. These shortcodes are also used to provide links to headings inside of your content, whether across documents or within a document. The only difference between `ref` and `relref` is whether the resulting URL is absolute (`http://1.com/about/`) or relative (`/about/`), respectively.
|
||||
|
||||
## Use `ref` and `relref`
|
||||
|
||||
```
|
||||
{{</* ref "document.md" */>}}
|
||||
{{</* ref "#anchor" */>}}
|
||||
{{</* ref "document.md#anchor" */>}}
|
||||
{{</* relref "document.md" */>}}
|
||||
{{</* relref "#anchor" */>}}
|
||||
{{</* relref "document.md#anchor" */>}}
|
||||
```
|
||||
|
||||
The single parameter to `ref` is a string with a content `documentname` (e.g., `about.md`) with or without an appended in-document `anchor` (`#who`) without spaces.
|
||||
|
||||
### Document Names
|
||||
|
||||
The `documentname` is the name of a document, including the format extension; this may be just the filename, or the relative path from the `content/` directory. With a document `content/blog/post.md`, either format will produce the same result:
|
||||
|
||||
```
|
||||
{{</* relref "blog/post.md" */>}} => `/blog/post/`
|
||||
{{</* relref "post.md" */>}} => `/blog/post/`
|
||||
```
|
||||
|
||||
If you have the same filename used across multiple sections, you should only use the relative path format; otherwise, the behavior will be `undefined`. This is best illustrated with an example `content` directory:
|
||||
|
||||
```
|
||||
.
|
||||
└── content
|
||||
├── events
|
||||
│ └── my-birthday.md
|
||||
├── galleries
|
||||
│ └── my-birthday.md
|
||||
├── meta
|
||||
│ └── my-article.md
|
||||
└── posts
|
||||
└── my-birthday.md
|
||||
```
|
||||
|
||||
To be sure to get the correct reference in this case, use the full path:
|
||||
|
||||
{{< code file="content/meta/my-article.md" copy="false" >}}
|
||||
{{</* relref "events/my-birthday.md" */>}} => /events/my-birthday/
|
||||
{{< /code >}}
|
||||
|
||||
### With Multiple Output Formats
|
||||
|
||||
If the page exists in multiple [output formats][], `ref` or `relref` can be used with a output format name:
|
||||
|
||||
```
|
||||
[Neat]({{</* ref "blog/neat.md" "amp" */>}})
|
||||
```
|
||||
|
||||
### Anchors
|
||||
|
||||
When an `anchor` is provided by itself, the current page’s unique identifier will be appended; when an `anchor` is provided appended to `documentname`, the found page's unique identifier will be appended:
|
||||
|
||||
```
|
||||
{{</* relref "#anchors" */>}} => #anchors:9decaf7
|
||||
{{</* relref "about-hugo/hugo-features.md#content" */>}} => /blog/post/#who:badcafe
|
||||
```
|
||||
|
||||
The above examples render as follows for this very page as well as a reference to the "Content" heading in the Hugo docs features pageyoursite
|
||||
|
||||
```
|
||||
{{</* relref "#who" */>}} => #who:9decaf7
|
||||
{{</* relref "blog/post.md#who" */>}} => /blog/post/#who:badcafe
|
||||
```
|
||||
|
||||
More information about document unique identifiers and headings can be found [below]({{< ref "#hugo-heading-anchors" >}}).
|
||||
|
||||
### Examples
|
||||
|
||||
* `{{</* ref "blog/post.md" */>}}` => `https://example.com/blog/post/`
|
||||
* `{{</* ref "post.md#tldr" */>}}` => `https://example.com/blog/post/#tldr:caffebad`
|
||||
* `{{</* relref "post.md" */>}}` => `/blog/post/`
|
||||
* `{{</* relref "blog/post.md#tldr" */>}}` => `/blog/post/#tldr:caffebad`
|
||||
* `{{</* ref "#tldr" */>}}` => `#tldr:badcaffe`
|
||||
* `{{</* relref "#tldr" */>}}` => `#tldr:badcaffe`
|
||||
|
||||
## Hugo Heading Anchors
|
||||
|
||||
When using Markdown document types, Hugo generates heading anchors automatically. The generated anchor for this section is `hugo-heading-anchors`. Because the heading anchors are generated automatically, Hugo takes some effort to ensure that heading anchors are unique both inside a document and across the entire site.
|
||||
|
||||
Ensuring heading uniqueness across the site is accomplished with a unique identifier for each document based on its path. Unless a document is renamed or moved between sections *in the filesystem*, the unique identifier for the document will not change: `blog/post.md` will always have a unique identifier of `81df004c333b392d34a49fd3a91ba720`.
|
||||
|
||||
`ref` and `relref` were added so you can make these reference links without having to know the document’s unique identifier. (The links in document tables of contents are automatically up-to-date with this value.)
|
||||
|
||||
```
|
||||
{{</* relref "content-management/cross-references.md#hugo-heading-anchors" */>}}
|
||||
/content-management/cross-references/#hugo-heading-anchors:77cd9ea530577debf4ce0f28c8dca242
|
||||
```
|
||||
|
||||
### Manually Specifying Anchors
|
||||
|
||||
For Markdown content files, if the `headerIds` [Blackfriday extension][bfext] is
|
||||
enabled (which it is by default), user can manually specify the anchor for any
|
||||
heading.
|
||||
|
||||
Few examples:
|
||||
|
||||
```
|
||||
## Alpha 101 {#alpha}
|
||||
|
||||
## Version 1.0 {#version-1-dot-0}
|
||||
```
|
||||
|
||||
[built-in Hugo shortcodes]: /content-management/shortcodes/#using-the-built-in-shortcodes
|
||||
[lists]: /templates/lists/
|
||||
[output formats]: /templates/output-formats/
|
||||
[shortcode]: /content-management/shortcodes/
|
||||
[bfext]: /content-management/formats/#blackfriday-extensions
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
title: Static Files
|
||||
description: "The `static` folder is where you place all your **static files**."
|
||||
date: 2017-11-18
|
||||
categories: [content management]
|
||||
keywords: [source, directories]
|
||||
menu:
|
||||
docs:
|
||||
parent: "content-management"
|
||||
weight: 130
|
||||
weight: 130 #rem
|
||||
aliases: [/static-files]
|
||||
toc: true
|
||||
---
|
||||
|
||||
The `static` folder is where you place all your **static files**, e.g. stylesheets, JavaScript, images etc.
|
||||
|
||||
You can set the name of the static folder to use in your configuration file. From **Hugo 0.31** you can configure as many static directories as you need. All the files in all the static directories will form a union filesystem.
|
||||
|
||||
Example:
|
||||
|
||||
{{< code-toggle copy="false" file="config" >}}
|
||||
staticDir = ["static1", "static2"]
|
||||
[languages]
|
||||
[languages.no]
|
||||
staticDir = ["staticDir_override", "static_no"]
|
||||
baseURL = "https://example.no"
|
||||
languageName = "Norsk"
|
||||
weight = 1
|
||||
title = "På norsk"
|
||||
|
||||
[languages.en]
|
||||
staticDir2 = "static_en"
|
||||
baseURL = "https://example.com"
|
||||
languageName = "English"
|
||||
weight = 2
|
||||
title = "In English"
|
||||
{{</ code-toggle >}}
|
||||
|
||||
In the above, with no theme used:
|
||||
|
||||
* The English site will get its static files as a union of "static1", "static2" and "static_en". On file duplicates, the right-most version will win.
|
||||
* The Norwegian site will get its static files as a union of "staticDir_override" and "static_no".
|
||||
|
||||
**Note:** The `2` `static2` (can be a number between 0 and 10) is added to tell Hugo that you want to **add** this directory to the global set of static directories. Using `staticDir` on the language level would replace the global value.
|
||||
|
||||
|
||||
**Note:** The example above is a [multihost setup](/content-management/multilingual/#configure-multilingual-multihost). In a regular setup, all the static directories will be available to all sites.
|
||||
@@ -33,7 +33,7 @@ features:
|
||||
copy: Hugo allows you to output your content in multiple formats, including JSON or AMP, and makes it easy to create your own.
|
||||
sections:
|
||||
- heading: "100s of Themes"
|
||||
cta: Check out the Hugo's themes.
|
||||
cta: Check out the Hugo themes.
|
||||
link: http://themes.gohugo.io/
|
||||
color_classes: bg-accent-color white
|
||||
image: /images/homepage-screenshot-hugo-themes.jpg
|
||||
@@ -12,7 +12,6 @@ menu:
|
||||
weight: 20
|
||||
sections_weight: 20
|
||||
draft: false
|
||||
aliases: [/about/features]
|
||||
toc: true
|
||||
---
|
||||
|
||||
@@ -53,12 +52,13 @@ toc: true
|
||||
* Integrated [Google Analytics][] support
|
||||
* Automatic [RSS][] creation
|
||||
* Support for [Go][], [Amber], and [Ace][] HTML templates
|
||||
* [Syntax highlighting][] powered by [Pygments][]
|
||||
* [Syntax highlighting][] powered by [Chroma][] (partly compatible with Pygments)
|
||||
|
||||
|
||||
[Ace]: /templates/alternatives/
|
||||
[aliases]: /content-management/urls/#aliases
|
||||
[Amber]: https://github.com/eknkc/amber
|
||||
[Chroma]: https://github.com/alecthomas/chroma
|
||||
[content summaries]: /content-management/summaries/
|
||||
[content types]: /content-management/types/
|
||||
[Disqus]: https://disqus.com/
|
||||
@@ -77,7 +77,6 @@ toc: true
|
||||
[Permalink]: /content-management/urls/#permalinks
|
||||
[Powerful theming]: /themes/
|
||||
[Pretty URLs]: /content-management/urls/
|
||||
[Pygments]: http://pygments.org/
|
||||
[RSS]: /templates/rss/
|
||||
[Shortcodes]: /content-management/shortcodes/
|
||||
[sort content]: /templates/
|
||||
@@ -0,0 +1,135 @@
|
||||
|
||||
|
||||
---
|
||||
title: Hugo and the General Data Protection Regulation (GDPR)
|
||||
linktitle: Hugo and GDPR
|
||||
description: About how to configure your Hugo site to meet the new regulations.
|
||||
date: 2018-05-25
|
||||
layout: single
|
||||
keywords: ["GDPR", "Privacy", "Data Protection"]
|
||||
menu:
|
||||
docs:
|
||||
parent: "about"
|
||||
weight: 5
|
||||
weight: 5
|
||||
sections_weight: 5
|
||||
draft: false
|
||||
aliases: [/privacy/,/gdpr/]
|
||||
toc: true
|
||||
---
|
||||
|
||||
|
||||
General Data Protection Regulation ([GDPR](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation)) is a regulation in EU law on data protection and privacy for all individuals within the European Union and the European Economic Area. It became enforceable on 25 May 2018.
|
||||
|
||||
**Hugo is a static site generator. By using Hugo you are already standing on very solid ground. Static HTML files on disk are much easier to reason about compared to server and database driven web sites.**
|
||||
|
||||
But even static websites can integrate with external services, so from version `0.41`, Hugo provides a **Privacy Config** that covers the relevant built-in templates.
|
||||
|
||||
Note that:
|
||||
|
||||
* These settings have their defaults setting set to _off_, i.e. how it worked before Hugo `0.41`. You must do your own evaluation of your site and apply the appropriate settings.
|
||||
* These settings work with the [internal templates](/templates/internal/). Some theme may contain custom templates for embedding services like Google Analytics. In that case these options have no effect.
|
||||
* We will continue this work and improve this further in future Hugo versions.
|
||||
|
||||
## 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`).
|
||||
|
||||
{{< code-toggle file="config">}}
|
||||
[privacy]
|
||||
[privacy.disqus]
|
||||
disable = false
|
||||
[privacy.googleAnalytics]
|
||||
disable = false
|
||||
respectDoNotTrack = false
|
||||
anonymizeIP = false
|
||||
useSessionStorage = false
|
||||
[privacy.instagram]
|
||||
disable = false
|
||||
simple = false
|
||||
[privacy.twitter]
|
||||
disable = false
|
||||
enableDNT = false
|
||||
simple = false
|
||||
[privacy.vimeo]
|
||||
disable = false
|
||||
simple = false
|
||||
[privacy.youtube]
|
||||
disable = false
|
||||
privacyEnhanced = false
|
||||
{{< /code-toggle >}}
|
||||
|
||||
|
||||
## Disable All Services
|
||||
|
||||
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">}}
|
||||
[privacy]
|
||||
[privacy.disqus]
|
||||
disable = true
|
||||
[privacy.googleAnalytics]
|
||||
disable = true
|
||||
[privacy.instagram]
|
||||
disable = true
|
||||
[privacy.twitter]
|
||||
disable = true
|
||||
[privacy.vimeo]
|
||||
disable = true
|
||||
[privacy.youtube]
|
||||
disable = true
|
||||
{{< /code-toggle >}}
|
||||
|
||||
## The Privacy Settings Explained
|
||||
|
||||
### GoogleAnalytics
|
||||
|
||||
anonymizeIP
|
||||
: Enabling this will make it so the users' IP addresses are anonymized within Google Analytics.
|
||||
|
||||
respectDoNotTrack
|
||||
: Enabling this will make the GA templates respect the "Do Not Track" HTTP header.
|
||||
|
||||
useSessionStorage
|
||||
: Enabling this will disable the use of Cookies and use Session Storage to Store the GA Client ID.
|
||||
|
||||
### Instagram
|
||||
|
||||
simple
|
||||
: If simple mode is enabled, a static and no-JS version of the Instagram image card will be built. Note that this only supports image cards and the image itself will be fetched from Instagram's servers.
|
||||
|
||||
**Note:** If you use the _simple mode_ for Instagram and a site styled with Bootstrap 4, you may want to disable the inlines styles provided by Hugo:
|
||||
|
||||
{{< code-toggle file="config">}}
|
||||
[services]
|
||||
[services.instagram]
|
||||
disableInlineCSS = true
|
||||
{{< /code-toggle >}}
|
||||
|
||||
### Twitter
|
||||
|
||||
enableDNT
|
||||
: Enabling this for the twitter/tweet shortcode, the tweet and its embedded page on your site are not used for purposes that include personalized suggestions and personalized ads.
|
||||
|
||||
simple
|
||||
: If simple mode is enabled, a static and no-JS version of a tweet will be built.
|
||||
|
||||
|
||||
**Note:** If you use the _simple mode_ for Twitter, you may want to disable the inlines styles provided by Hugo:
|
||||
|
||||
{{< code-toggle file="config">}}
|
||||
[services]
|
||||
[services.twitter]
|
||||
disableInlineCSS = true
|
||||
{{< /code-toggle >}}
|
||||
|
||||
### YouTube
|
||||
|
||||
privacyEnhanced
|
||||
: When you turn on privacy-enhanced mode, YouTube won’t store information about visitors on your website unless the user plays the embedded video.
|
||||
|
||||
### Vimeo
|
||||
|
||||
simple
|
||||
: If simple mode is enabled, the video thumbnail is fetched from Vimeo's servers and it is overlayed with a play button. If the user clicks to play the video, it will open in a new tab directly on Vimeo's website.
|
||||
|
||||
@@ -24,7 +24,7 @@ This documentation belongs in other places in this documentation site, but is pu
|
||||
|
||||
Also see this demo project from [bep](https://github.com/bep/), the clever Norwegian behind these new features:
|
||||
|
||||
* http://hugotest.bep.is/
|
||||
* https://temp.bep.is/hugotest/
|
||||
* https://github.com/bep/hugotest (source)
|
||||
|
||||
## Page Resources
|
||||
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
@@ -43,27 +43,23 @@ Hugo is for people building a blog, a company site, a portfolio site, documentat
|
||||
|
||||
[@spf13]: https://twitter.com/@spf13
|
||||
[Aerobatic]: https://www.aerobatic.com/
|
||||
[Amazon S3]: http://aws.amazon.com/s3/
|
||||
[Azure]: https://blogs.msdn.microsoft.com/acoat/2016/01/28/publish-a-static-web-site-using-azure-web-apps/
|
||||
[CloudFront]: http://aws.amazon.com/cloudfront/ "Amazon CloudFront"
|
||||
[contributing to it]: https://github.com/gohugoio/hugo
|
||||
[DreamHost]: http://www.dreamhost.com/
|
||||
[Amazon S3]: https://aws.amazon.com/s3/
|
||||
[Azure]: https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website
|
||||
[CloudFront]: https://aws.amazon.com/cloudfront/ "Amazon CloudFront"
|
||||
[DreamHost]: https://www.dreamhost.com/
|
||||
[Firebase]: https://firebase.google.com/docs/hosting/ "Firebase static hosting"
|
||||
[GitHub Pages]: https://pages.github.com/
|
||||
[GitLab Pages]: https://about.gitlab.com/features/pages/
|
||||
[Go language]: https://golang.org/
|
||||
[GoDaddy]: https://www.godaddy.com/ "Godaddy.com Hosting"
|
||||
[Google Cloud Storage]: http://cloud.google.com/storage/
|
||||
[Google Cloud Storage]: https://cloud.google.com/storage/
|
||||
[Heroku]: https://www.heroku.com/
|
||||
[Jekyll]: http://jekyllrb.com/
|
||||
[Jekyll]: https://jekyllrb.com/
|
||||
[Middleman]: https://middlemanapp.com/
|
||||
[Middleman]: https://middlemanapp.com/
|
||||
[Nanoc]: http://nanoc.ws/
|
||||
[Nanoc]: https://nanoc.ws/
|
||||
[Netlify]: https://netlify.com
|
||||
[Rackspace]: https://www.rackspace.com/cloud/files
|
||||
[Surge]: https://surge.sh
|
||||
[contributing to it]: https://github.com/gohugoio/hugo
|
||||
[rackspace]: https://www.rackspace.com/cloud/files
|
||||
[static site generator]: /about/benefits/
|
||||
[Rackspace]: https://www.rackspace.com/cloud/files
|
||||
[static site generator]: /about/benefits/
|
||||
[Surge]: https://surge.sh
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
date: 2018-09-24
|
||||
title: "hugo"
|
||||
slug: hugo
|
||||
url: /commands/hugo/
|
||||
---
|
||||
## hugo
|
||||
|
||||
hugo builds your site
|
||||
|
||||
### Synopsis
|
||||
|
||||
hugo is the main command, used to build your Hugo site.
|
||||
|
||||
Hugo is a Fast and Flexible Static Site Generator
|
||||
built with love by spf13 and friends in Go.
|
||||
|
||||
Complete documentation is available at http://gohugo.io/.
|
||||
|
||||
```
|
||||
hugo [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. http://spf13.com/
|
||||
-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/
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
--config string config file (default is path/config.yaml|json|toml)
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--debug debug output
|
||||
-d, --destination string filesystem path to write files to
|
||||
--disableKinds strings disable different kind of pages (home, RSS etc.)
|
||||
--enableGitInfo add Git revision, date and author info to the pages
|
||||
--forceSyncStatic copy all files when static is changed.
|
||||
--gc enable to run some cleanup tasks (remove unused cache files) after the build
|
||||
-h, --help help for hugo
|
||||
--i18n-warnings print missing translations
|
||||
--ignoreCache ignores the cache directory
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--minify minify any supported output format (HTML, XML etc.)
|
||||
--noChmod don't sync permission mode of files
|
||||
--noTimes don't sync modification time of files
|
||||
--quiet build in quiet mode
|
||||
--renderToMemory render to memory (only useful for benchmark testing)
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--stepAnalysis display memory and timing of different steps of the program
|
||||
--templateMetrics display metrics about template executions
|
||||
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
|
||||
-t, --theme string theme to use (located in /themes/THEMENAME/)
|
||||
--themesDir string filesystem path to themes directory
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
-w, --watch watch filesystem for changes and recreate as needed
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo benchmark](/commands/hugo_benchmark/) - Benchmark Hugo by building a site a number of times.
|
||||
* [hugo check](/commands/hugo_check/) - Contains some verification checks
|
||||
* [hugo config](/commands/hugo_config/) - Print the site configuration
|
||||
* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
|
||||
* [hugo env](/commands/hugo_env/) - Print Hugo version and environment info
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
* [hugo import](/commands/hugo_import/) - Import your site from others.
|
||||
* [hugo list](/commands/hugo_list/) - Listing out various types of content
|
||||
* [hugo new](/commands/hugo_new/) - Create new content for your site
|
||||
* [hugo server](/commands/hugo_server/) - A high performance webserver
|
||||
* [hugo version](/commands/hugo_version/) - Print the version number of Hugo
|
||||
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
date: 2018-09-24
|
||||
title: "hugo benchmark"
|
||||
slug: hugo_benchmark
|
||||
url: /commands/hugo_benchmark/
|
||||
---
|
||||
## hugo benchmark
|
||||
|
||||
Benchmark Hugo by building a site a number of times.
|
||||
|
||||
### Synopsis
|
||||
|
||||
Hugo can build a site many times over and analyze the running process
|
||||
creating a benchmark.
|
||||
|
||||
```
|
||||
hugo benchmark [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. http://spf13.com/
|
||||
-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/
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-n, --count int number of times to build the site (default 13)
|
||||
--cpuprofile string path/filename for the CPU profile file
|
||||
-d, --destination string filesystem path to write files to
|
||||
--disableKinds strings disable different kind of pages (home, RSS etc.)
|
||||
--enableGitInfo add Git revision, date and author info to the pages
|
||||
--forceSyncStatic copy all files when static is changed.
|
||||
--gc enable to run some cleanup tasks (remove unused cache files) after the build
|
||||
-h, --help help for benchmark
|
||||
--i18n-warnings print missing translations
|
||||
--ignoreCache ignores the cache directory
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--memprofile string path/filename for the memory profile file
|
||||
--noChmod don't sync permission mode of files
|
||||
--noTimes don't sync modification time of files
|
||||
--renderToMemory render to memory (only useful for benchmark testing)
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--stepAnalysis display memory and timing of different steps of the program
|
||||
--templateMetrics display metrics about template executions
|
||||
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
|
||||
-t, --theme string theme to use (located in /themes/THEMENAME/)
|
||||
--themesDir string filesystem path to themes directory
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string config file (default is path/config.yaml|json|toml)
|
||||
--debug debug output
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--quiet build in quiet mode
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo check"
|
||||
slug: hugo_check
|
||||
url: /commands/hugo_check/
|
||||
@@ -35,4 +35,4 @@ Contains some verification checks
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
* [hugo check ulimit](/commands/hugo_check_ulimit/) - Check system ulimit settings
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo check ulimit"
|
||||
slug: hugo_check_ulimit
|
||||
url: /commands/hugo_check_ulimit/
|
||||
@@ -39,4 +39,4 @@ hugo check ulimit [flags]
|
||||
|
||||
* [hugo check](/commands/hugo_check/) - Contains some verification checks
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo config"
|
||||
slug: hugo_config
|
||||
url: /commands/hugo_config/
|
||||
@@ -19,7 +19,8 @@ hugo config [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for config
|
||||
-h, --help help for config
|
||||
-s, --source string filesystem path to read files relative from
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -38,4 +39,4 @@ hugo config [flags]
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo convert"
|
||||
slug: hugo_convert
|
||||
url: /commands/hugo_convert/
|
||||
@@ -42,4 +42,4 @@ See convert's subcommands toJSON, toTOML and toYAML for more information.
|
||||
* [hugo convert toTOML](/commands/hugo_convert_totoml/) - Convert front matter to TOML
|
||||
* [hugo convert toYAML](/commands/hugo_convert_toyaml/) - Convert front matter to YAML
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo convert toJSON"
|
||||
slug: hugo_convert_toJSON
|
||||
url: /commands/hugo_convert_tojson/
|
||||
@@ -42,4 +42,4 @@ hugo convert toJSON [flags]
|
||||
|
||||
* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo convert toTOML"
|
||||
slug: hugo_convert_toTOML
|
||||
url: /commands/hugo_convert_totoml/
|
||||
@@ -42,4 +42,4 @@ hugo convert toTOML [flags]
|
||||
|
||||
* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo convert toYAML"
|
||||
slug: hugo_convert_toYAML
|
||||
url: /commands/hugo_convert_toyaml/
|
||||
@@ -42,4 +42,4 @@ hugo convert toYAML [flags]
|
||||
|
||||
* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo env"
|
||||
slug: hugo_env
|
||||
url: /commands/hugo_env/
|
||||
@@ -38,4 +38,4 @@ hugo env [flags]
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo gen"
|
||||
slug: hugo_gen
|
||||
url: /commands/hugo_gen/
|
||||
@@ -38,4 +38,4 @@ A collection of several useful generators.
|
||||
* [hugo gen doc](/commands/hugo_gen_doc/) - Generate Markdown documentation for the Hugo CLI.
|
||||
* [hugo gen man](/commands/hugo_gen_man/) - Generate man pages for the Hugo CLI
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo gen autocomplete"
|
||||
slug: hugo_gen_autocomplete
|
||||
url: /commands/hugo_gen_autocomplete/
|
||||
@@ -56,4 +56,4 @@ hugo gen autocomplete [flags]
|
||||
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo gen chromastyles"
|
||||
slug: hugo_gen_chromastyles
|
||||
url: /commands/hugo_gen_chromastyles/
|
||||
@@ -43,4 +43,4 @@ hugo gen chromastyles [flags]
|
||||
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo gen doc"
|
||||
slug: hugo_gen_doc
|
||||
url: /commands/hugo_gen_doc/
|
||||
@@ -45,4 +45,4 @@ hugo gen doc [flags]
|
||||
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo gen man"
|
||||
slug: hugo_gen_man
|
||||
url: /commands/hugo_gen_man/
|
||||
@@ -41,4 +41,4 @@ hugo gen man [flags]
|
||||
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo import"
|
||||
slug: hugo_import
|
||||
url: /commands/hugo_import/
|
||||
@@ -37,4 +37,4 @@ Import requires a subcommand, e.g. `hugo import jekyll jekyll_root_path target_p
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
* [hugo import jekyll](/commands/hugo_import_jekyll/) - hugo import from Jekyll
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo import jekyll"
|
||||
slug: hugo_import_jekyll
|
||||
url: /commands/hugo_import_jekyll/
|
||||
@@ -41,4 +41,4 @@ hugo import jekyll [flags]
|
||||
|
||||
* [hugo import](/commands/hugo_import/) - Import your site from others.
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo list"
|
||||
slug: hugo_list
|
||||
url: /commands/hugo_list/
|
||||
@@ -40,4 +40,4 @@ List requires a subcommand, e.g. `hugo list drafts`.
|
||||
* [hugo list expired](/commands/hugo_list_expired/) - List all posts already expired
|
||||
* [hugo list future](/commands/hugo_list_future/) - List all posts dated in the future
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo list drafts"
|
||||
slug: hugo_list_drafts
|
||||
url: /commands/hugo_list_drafts/
|
||||
@@ -39,4 +39,4 @@ hugo list drafts [flags]
|
||||
|
||||
* [hugo list](/commands/hugo_list/) - Listing out various types of content
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo list expired"
|
||||
slug: hugo_list_expired
|
||||
url: /commands/hugo_list_expired/
|
||||
@@ -40,4 +40,4 @@ hugo list expired [flags]
|
||||
|
||||
* [hugo list](/commands/hugo_list/) - Listing out various types of content
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo list future"
|
||||
slug: hugo_list_future
|
||||
url: /commands/hugo_list_future/
|
||||
@@ -40,4 +40,4 @@ hugo list future [flags]
|
||||
|
||||
* [hugo list](/commands/hugo_list/) - Listing out various types of content
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo new"
|
||||
slug: hugo_new
|
||||
url: /commands/hugo_new/
|
||||
@@ -48,4 +48,4 @@ hugo new [path] [flags]
|
||||
* [hugo new site](/commands/hugo_new_site/) - Create a new site (skeleton)
|
||||
* [hugo new theme](/commands/hugo_new_theme/) - Create a new theme
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo new site"
|
||||
slug: hugo_new_site
|
||||
url: /commands/hugo_new_site/
|
||||
@@ -43,4 +43,4 @@ hugo new site [path] [flags]
|
||||
|
||||
* [hugo new](/commands/hugo_new/) - Create new content for your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo new theme"
|
||||
slug: hugo_new_theme
|
||||
url: /commands/hugo_new_theme/
|
||||
@@ -42,4 +42,4 @@ hugo new theme [name] [flags]
|
||||
|
||||
* [hugo new](/commands/hugo_new/) - Create new content for your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
date: 2018-09-24
|
||||
title: "hugo server"
|
||||
slug: hugo_server
|
||||
url: /commands/hugo_server/
|
||||
---
|
||||
## hugo server
|
||||
|
||||
A high performance webserver
|
||||
|
||||
### Synopsis
|
||||
|
||||
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.
|
||||
|
||||
By default hugo will also watch your files for any changes you make and
|
||||
automatically rebuild the site. It will then live reload any open browser pages
|
||||
and push the latest content to them. As most Hugo sites are built in a fraction
|
||||
of a second, you will be able to save and see your changes nearly instantly.
|
||||
|
||||
```
|
||||
hugo server [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--appendPort append port to baseURL (default true)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. http://spf13.com/
|
||||
--bind string interface to which the server will bind (default "127.0.0.1")
|
||||
-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/
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-d, --destination string filesystem path to write files to
|
||||
--disableFastRender enables full re-renders on changes
|
||||
--disableKinds strings disable different kind of pages (home, RSS etc.)
|
||||
--disableLiveReload watch without enabling live browser reload on rebuild
|
||||
--enableGitInfo add Git revision, date and author info to the pages
|
||||
--forceSyncStatic copy all files when static is changed.
|
||||
--gc enable to run some cleanup tasks (remove unused cache files) after the build
|
||||
-h, --help help for server
|
||||
--i18n-warnings print missing translations
|
||||
--ignoreCache ignores the cache directory
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--liveReloadPort int port for live reloading (i.e. 443 in HTTPS proxy situations) (default -1)
|
||||
--meminterval string interval to poll memory usage (requires --memstats), valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". (default "100ms")
|
||||
--memstats string log memory usage to this file
|
||||
--navigateToChanged navigate to changed content file on live browser reload
|
||||
--noChmod don't sync permission mode of files
|
||||
--noHTTPCache prevent HTTP caching
|
||||
--noTimes don't sync modification time of files
|
||||
-p, --port int port on which the server will listen (default 1313)
|
||||
--renderToDisk render to Destination path (default is render to memory & serve from there)
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--stepAnalysis display memory and timing of different steps of the program
|
||||
--templateMetrics display metrics about template executions
|
||||
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
|
||||
-t, --theme string theme to use (located in /themes/THEMENAME/)
|
||||
--themesDir string filesystem path to themes directory
|
||||
-w, --watch watch filesystem for changes and recreate as needed (default true)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string config file (default is path/config.yaml|json|toml)
|
||||
--debug debug output
|
||||
--log enable Logging
|
||||
--logFile string log File path (if set, logging enabled automatically)
|
||||
--quiet build in quiet mode
|
||||
-v, --verbose verbose output
|
||||
--verboseLog verbose logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
date: 2018-09-24
|
||||
title: "hugo version"
|
||||
slug: hugo_version
|
||||
url: /commands/hugo_version/
|
||||
@@ -38,4 +38,4 @@ hugo version [flags]
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
###### Auto generated by spf13/cobra on 24-Sep-2018
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user