mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-25 15:58:53 +00:00
Compare commits
341 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 933a16b109 | |||
| 60845a5a0e | |||
| 9a39146edf | |||
| 1a6a0fa6f2 | |||
| 936fc76ad6 | |||
| f21b827f7b | |||
| 7c597c7d75 | |||
| 047c4188df | |||
| e136c26d0c | |||
| dd48277497 | |||
| 4dba6ce15a | |||
| 880ca19f20 | |||
| 01b72eb592 | |||
| 61d52f1462 | |||
| 0e7716a424 | |||
| 417c5e2b67 | |||
| 94c8b29c39 | |||
| 1b4e0c4161 | |||
| 27a524b090 | |||
| bede93de00 | |||
| 2aab6dee85 | |||
| f3775877c6 | |||
| 9c782d5147 | |||
| 14c35c8a56 | |||
| 96689a5c31 | |||
| e7010c1b62 | |||
| f396cffa23 | |||
| 24d5c21942 | |||
| a7d00fc39e | |||
| 1e233b1c45 | |||
| b110d0ae04 | |||
| 73825cfc1c | |||
| a8f7fbbb10 | |||
| e8d6ca9531 | |||
| 4d32f2fa89 | |||
| 018602c46d | |||
| 2f0d98a19b | |||
| e0621d207c | |||
| e26a8b242a | |||
| 2a2c983867 | |||
| 15b1e269ad | |||
| 56a1308044 | |||
| 4b780ca778 | |||
| 7bc5e89fba | |||
| fdf1d94ebc | |||
| 1157fef859 | |||
| e614d8a57c | |||
| fca49d6c60 | |||
| 0e8b3cbcd2 | |||
| 230f2b8c4f | |||
| d7f47b780c | |||
| 712c0fdfe6 | |||
| 62e7588262 | |||
| 47da0dca52 | |||
| 8d87bfd5ee | |||
| 719251c1af | |||
| 8b2ef83bb1 | |||
| 080302eb87 | |||
| 094ec17142 | |||
| e84b7ade65 | |||
| 2817e84240 | |||
| 623c9afa84 | |||
| 2170943ae6 | |||
| 9cd4d35977 | |||
| 3a3261a3b9 | |||
| 6792d86ad0 | |||
| e7d87e2410 | |||
| c475eab474 | |||
| 3a96fa40bf | |||
| c356f1d417 | |||
| 30c2c2f8ec | |||
| f87239e4ca | |||
| db0633c1a4 | |||
| 874159b543 | |||
| 294c0f8001 | |||
| a4deaeff0c | |||
| 4d115c56fa | |||
| 26f34fd59d | |||
| 730b66b652 | |||
| 4f639d6bd5 | |||
| f7bc28c529 | |||
| 20e9c08e1f | |||
| 37140a093b | |||
| da80163c75 | |||
| 60e66d12ea | |||
| ed8bf081fd | |||
| 558825cc20 | |||
| 10c33c17cd | |||
| 2c54f1ad48 | |||
| eb15ac37ef | |||
| eb42774e58 | |||
| f27977809c | |||
| 7a634898c3 | |||
| 1823c053c8 | |||
| 511d5d3b76 | |||
| 8299fa885c | |||
| 06495f713b | |||
| 93e24a03ce | |||
| e9c7b6205f | |||
| 3d1a6e109c | |||
| b6798ee867 | |||
| 0a23baa6a9 | |||
| 84f4b731d2 | |||
| 61f6e9f63b | |||
| 904a3d9ddf | |||
| f0052b6d0f | |||
| da88015776 | |||
| debd3663dd | |||
| af67ad8af1 | |||
| e46ab29bd2 | |||
| 10fef32de6 | |||
| 70005364a2 | |||
| ffaec4ca8c | |||
| 3886fc1fef | |||
| 91fb8f1b59 | |||
| ac12d51e7e | |||
| 3fbc75534d | |||
| e427ba4268 | |||
| 79dd7cb31a | |||
| 0dbf79c2f8 | |||
| f12ad7d56a | |||
| ce6e4310fe | |||
| 95d62004a0 | |||
| c029065582 | |||
| 68bf1511f2 | |||
| f8dc47eeff | |||
| ae3fa349de | |||
| 64cec07293 | |||
| 9ac6206584 | |||
| f414966b94 | |||
| c29a2ed2f5 | |||
| dfb6484a41 | |||
| 8a02968393 | |||
| e7fda289b1 | |||
| 50a03a5acc | |||
| ba94abbf5d | |||
| 55fb0eac20 | |||
| 06e5ee6526 | |||
| 956a2dce8d | |||
| 1f1c562b9c | |||
| 3e1a6ebc11 | |||
| 8915cb83f4 | |||
| 900b5f6cfe | |||
| 57637d89ec | |||
| 13ea1e7c35 | |||
| 9f740b37cf | |||
| e39797fa72 | |||
| 00868081f6 | |||
| e9750d831f | |||
| 19d9a46f63 | |||
| 0602135fd4 | |||
| 55bd46a633 | |||
| 8dc77b84a5 | |||
| 4560261b58 | |||
| 374d184e67 | |||
| 772128485a | |||
| c2c4cb1f9f | |||
| 492fea7cd2 | |||
| 5ed11edf5a | |||
| 799c654b0d | |||
| faa3159e5e | |||
| eaf573a277 | |||
| 3ced6f8d47 | |||
| a3f26e5636 | |||
| 0c452c6e7d | |||
| ff10c15a93 | |||
| efd506314a | |||
| 8de91da74f | |||
| 25e88ccabe | |||
| 19228ed83c | |||
| a1f40084f9 | |||
| d7bf9d4daa | |||
| 53e661e7c9 | |||
| fc23a80ffd | |||
| d8fdffb552 | |||
| 2851af0225 | |||
| 08521dac83 | |||
| 58382e9572 | |||
| 53dac9a506 | |||
| 4edb4bcc65 | |||
| 0816a97a46 | |||
| 10a917dfdc | |||
| d4beef0d2b | |||
| 51213e0be1 | |||
| 1fa2417777 | |||
| bb549a0d57 | |||
| 82eefded13 | |||
| e172834993 | |||
| b5cb8866e9 | |||
| 9cc9bab462 | |||
| d382502d6d | |||
| 2e95ec6844 | |||
| 76d38d5e53 | |||
| f554503fef | |||
| 16a5c74519 | |||
| 49b98975be | |||
| 0de375a7e7 | |||
| e02ee7d6ad | |||
| 18e3657a2a | |||
| 4bc8727fc6 | |||
| a9a0190b13 | |||
| 742deaa3d0 | |||
| 694c0fd763 | |||
| 15ff076572 | |||
| c305e44f5f | |||
| 722086b4ed | |||
| 084cf4191b | |||
| fc06d5c18b | |||
| 9a367d9d06 | |||
| 4402c07775 | |||
| 4743de0d3c | |||
| 19e26cb4eb | |||
| 0283db6c60 | |||
| 58188807aa | |||
| 51d5c5421e | |||
| a9a5b1e094 | |||
| cf5c2df81c | |||
| 158e1151cd | |||
| 337d0c5f51 | |||
| f0c0ece44d | |||
| a34213f0b5 | |||
| fe40823de0 | |||
| ae742cb1bd | |||
| feeed073c3 | |||
| 94e736c5e1 | |||
| 1707dae8d3 | |||
| 2fa70c9344 | |||
| b6f3f087aa | |||
| adfd4370b6 | |||
| 25d691daff | |||
| df20b05463 | |||
| 863a812e07 | |||
| f08ea02d24 | |||
| 016398ffe2 | |||
| 3752348ef1 | |||
| 4eb2fec67c | |||
| 83c761b71a | |||
| ed4a00e46f | |||
| 7b472e4608 | |||
| 96e3fbcf23 | |||
| a91aba1c15 | |||
| 6413559f75 | |||
| 322c567220 | |||
| 91bb774ae4 | |||
| 3f0379adb7 | |||
| ae5a45be6f | |||
| 4d912e2aad | |||
| 1e27d05891 | |||
| d418c2c2ea | |||
| b2fcbb1f97 | |||
| 3446fe9b89 | |||
| 4a7c2b3695 | |||
| 912147ab89 | |||
| 517b6b6238 | |||
| a19563910e | |||
| 0432c64dd2 | |||
| 5a0819b9b5 | |||
| 78c863305f | |||
| 22ad1ebdbb | |||
| ebdd8cba3f | |||
| 8125b4b03d | |||
| 87217d1d0a | |||
| ffd7dc396e | |||
| 2228d3e289 | |||
| 5903a9ce10 | |||
| 4d644c9d68 | |||
| 34a216fe67 | |||
| adee04f4b4 | |||
| 9421380168 | |||
| 5d03086981 | |||
| 07700aab0d | |||
| 4d5e4f379a | |||
| cd77968284 | |||
| c7b9347be0 | |||
| c6b9037294 | |||
| 09a934b321 | |||
| e6cc2a6551 | |||
| a39ae4bb07 | |||
| b3eeb97642 | |||
| 3cf4300097 | |||
| 9409bc0f79 | |||
| 4eb1650bec | |||
| 20c9b6ec81 | |||
| f8a119b606 | |||
| ecaf14514e | |||
| d4f8f88e67 | |||
| 4e524ffcff | |||
| 64f0e9d1c1 | |||
| 13d53b31f1 | |||
| 51dd462c39 | |||
| b6ea6d07d0 | |||
| 4dac1781e3 | |||
| 8a409894bd | |||
| 46db900dab | |||
| 60c9f3b1c3 | |||
| 57e10f174e | |||
| db85e83403 | |||
| 1046e9363f | |||
| d5803da1be | |||
| 27c77e1ab9 | |||
| 218773a82b | |||
| a97ef61bad | |||
| 4aaf27dc55 | |||
| f7d0b05f93 | |||
| f82428d2e0 | |||
| 50fb49c3d9 | |||
| 5235a5bf5e | |||
| 1921a70ab1 | |||
| f0eecc6a4f | |||
| 768ec5df9f | |||
| 238e7f7fbe | |||
| 0aaa38544d | |||
| fdec5e3084 | |||
| 71abb53fff | |||
| db5d79862b | |||
| 2d3189b227 | |||
| 4b04db0f08 | |||
| 97c1866e32 | |||
| 8969331f5b | |||
| 6feb138785 | |||
| 059e8458d6 | |||
| ab82a27d05 | |||
| f25d8a9e17 | |||
| 54a89cde69 | |||
| 2cd962233f | |||
| 865877cd05 | |||
| 1ff80a8bbe | |||
| 956ef40978 | |||
| 196da49c9d | |||
| 2aa4c009ee | |||
| eb738cd35c | |||
| 3dd8c1c026 | |||
| fd301c3c05 | |||
| 54ef60b03a | |||
| 14a758ff24 | |||
| e64eb5ec2b | |||
| d36d71edd3 | |||
| a54fd7df21 | |||
| 16e1d99c6d | |||
| bf8a61fde5 | |||
| 72158ab2e8 |
@@ -1,7 +1,7 @@
|
||||
defaults: &defaults
|
||||
working_directory: /go/src/github.com/gohugoio
|
||||
docker:
|
||||
- image: bepsays/ci-goreleaser:0.34.2-6
|
||||
- image: bepsays/ci-goreleaser:0.34.2-11
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 120
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 21
|
||||
daysUntilClose: 30
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- Keep
|
||||
@@ -20,4 +20,4 @@ markComment: >
|
||||
This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
closeComment: false
|
||||
|
||||
@@ -12,6 +12,7 @@ cover.out
|
||||
*~
|
||||
vendor/*/
|
||||
*.bench
|
||||
*.debug
|
||||
coverage*.out
|
||||
|
||||
GoBuilds
|
||||
|
||||
+10
-8
@@ -1,9 +1,11 @@
|
||||
language: go
|
||||
sudo: required
|
||||
dist: precise
|
||||
sudo: false
|
||||
dist: trusty
|
||||
git:
|
||||
depth: false
|
||||
go:
|
||||
- 1.8.5
|
||||
- 1.9.2
|
||||
- 1.9.5
|
||||
- "1.10.1"
|
||||
- tip
|
||||
os:
|
||||
- linux
|
||||
@@ -14,12 +16,12 @@ matrix:
|
||||
fast_finish: true
|
||||
install:
|
||||
- go get github.com/magefile/mage
|
||||
- mage vendor
|
||||
- mage -v vendor
|
||||
script:
|
||||
- mage hugoRace
|
||||
- mage -v hugoRace
|
||||
- mage -v check
|
||||
- ./hugo -s docs/
|
||||
- ./hugo --renderToMemory -s docs/
|
||||
before_install:
|
||||
# gem install must be run with sudo on OSX
|
||||
- sudo gem install asciidoctor | gem install asciidoctor
|
||||
- gem install asciidoctor
|
||||
- type asciidoctor
|
||||
|
||||
+61
-28
@@ -36,6 +36,23 @@ the GitHub [issue tracker](https://github.com/gohugoio/hugo/issues) to report th
|
||||
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:
|
||||
|
||||
* 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.).
|
||||
|
||||
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.
|
||||
|
||||
|
||||
**Bug fixes are, of course, always welcome.**
|
||||
|
||||
|
||||
|
||||
## Submitting Patches
|
||||
|
||||
The Hugo project welcomes all contributors and contributions regardless of skill or experience level.
|
||||
@@ -88,42 +105,42 @@ new default function more useful for Hugo users.
|
||||
Fixes #1949
|
||||
```
|
||||
|
||||
### Vendored Dependencies
|
||||
|
||||
Hugo uses [Go 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 since `go get` is not vendor-aware.
|
||||
|
||||
You **must use Go Dep** to fetch and manage Hugo's dependencies.
|
||||
|
||||
### Fetch the Sources From GitHub
|
||||
### 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:
|
||||
|
||||
1. Install Go Dep and get the Hugo source:
|
||||
1. Get the Hugo source:
|
||||
|
||||
```bash
|
||||
go get -u -v -d github.com/gohugoio/hugo
|
||||
```
|
||||
|
||||
1. Install Mage:
|
||||
|
||||
```bash
|
||||
go get github.com/magefile/mage
|
||||
```
|
||||
go get -u -v github.com/golang/dep/cmd/dep
|
||||
go get -u -v -d github.com/gohugoio/hugo
|
||||
```
|
||||
|
||||
1. Change to the Hugo source directory and fetch the dependencies:
|
||||
|
||||
```
|
||||
```bash
|
||||
cd $HOME/go/src/github.com/gohugoio/hugo
|
||||
dep ensure
|
||||
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.
|
||||
|
||||
1. Create a new branch for your changes (the branch name is arbitrary):
|
||||
|
||||
```
|
||||
```bash
|
||||
git checkout -b iss1234
|
||||
```
|
||||
|
||||
1. After making your changes, commit them to your new branch:
|
||||
|
||||
```
|
||||
```bash
|
||||
git commit -a -v
|
||||
```
|
||||
|
||||
@@ -131,42 +148,58 @@ started:
|
||||
|
||||
1. Add your fork as a new remote (the remote name, "fork" in this example, is arbitrary):
|
||||
|
||||
```
|
||||
```bash
|
||||
git remote add fork git://github.com/USERNAME/hugo.git
|
||||
```
|
||||
|
||||
1. Push the changes to your new remote:
|
||||
|
||||
```
|
||||
```bash
|
||||
git push --set-upstream fork iss1234
|
||||
```
|
||||
|
||||
1. You're now ready to submit a PR based upon the new branch in your forked repository.
|
||||
|
||||
### Build Hugo with Your Changes
|
||||
### Building Hugo with Your Changes
|
||||
|
||||
**Note:** Hugo uses [mage](https://github.com/magefile/mage) to build. To install `mage` run
|
||||
|
||||
```bash
|
||||
go get github.com/magefile/mage
|
||||
```
|
||||
|
||||
`mage -l` lists all available commands with the corresponding description. To build Hugo run
|
||||
Hugo uses [mage](https://github.com/magefile/mage) to sync vendor dependencies, build Hugo, run the test suite and other things. You must run mage from the Hugo directory.
|
||||
|
||||
```bash
|
||||
cd $HOME/go/src/github.com/gohugoio/hugo
|
||||
```
|
||||
|
||||
To build Hugo:
|
||||
|
||||
```bash
|
||||
mage hugo
|
||||
# or to install in $HOME/go/bin:
|
||||
```
|
||||
|
||||
To install hugo in `$HOME/go/bin`:
|
||||
|
||||
```bash
|
||||
mage install
|
||||
```
|
||||
|
||||
To run the tests:
|
||||
|
||||
```bash
|
||||
mage hugoRace
|
||||
mage -v check
|
||||
```
|
||||
|
||||
To list all available commands along with descriptions:
|
||||
|
||||
```bash
|
||||
mage -l
|
||||
```
|
||||
|
||||
### Updating the Hugo Sources
|
||||
|
||||
If you want to stay in sync with the Hugo repository, you can easily pull down
|
||||
the source changes, but you'll need to keep the vendored packages up-to-date as
|
||||
well.
|
||||
|
||||
```
|
||||
```bash
|
||||
git pull
|
||||
mage vendor
|
||||
```
|
||||
|
||||
+1
-1
@@ -4,8 +4,8 @@ RUN apk add --no-cache --virtual git musl-dev
|
||||
RUN go get github.com/golang/dep/cmd/dep
|
||||
|
||||
WORKDIR /go/src/github.com/gohugoio/hugo
|
||||
RUN dep ensure
|
||||
ADD . /go/src/github.com/gohugoio/hugo/
|
||||
RUN dep ensure
|
||||
RUN go install -ldflags '-s -w'
|
||||
|
||||
FROM alpine:3.6
|
||||
|
||||
Generated
+111
-48
@@ -20,32 +20,66 @@
|
||||
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 = "v0.2.0"
|
||||
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 = "de8030ebafb76c6e84d50ee6d143382637c00598"
|
||||
revision = "012701e8669671499fc43e9792335a1dcbfe2afb"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/chaseadamsio/goorgeous"
|
||||
packages = ["."]
|
||||
revision = "v1.1.0"
|
||||
revision = "dcf1ef873b8987bf12596fe6951c48347986eb2f"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cpuguy83/go-md2man"
|
||||
packages = ["md2man"]
|
||||
revision = "1d903dcb749992f3741d744c0f8376b4bd7eb3e1"
|
||||
version = "v1.0.7"
|
||||
revision = "20f5889cbdc3c73dbd2862796665e7c465ade7d1"
|
||||
version = "v1.0.8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -62,7 +96,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/disintegration/imaging"
|
||||
packages = ["."]
|
||||
revision = "v1.2.4"
|
||||
revision = "dd50a3ee9985ccd313a2f03c398fcaedc96dc707"
|
||||
version = "v1.2.4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/dlclark/regexp2"
|
||||
@@ -85,14 +120,29 @@
|
||||
[[projects]]
|
||||
name = "github.com/fortytw2/leaktest"
|
||||
packages = ["."]
|
||||
revision = "7dad53304f9614c1c365755c1176a8e876fee3e8"
|
||||
version = "v1.1.0"
|
||||
revision = "a5ef70473c97b71626b9abeda80ee92ba2a7de9e"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
packages = ["."]
|
||||
revision = "629574ca2a5df945712d3079857300b5e4da0236"
|
||||
version = "v1.4.2"
|
||||
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"
|
||||
@@ -104,13 +154,13 @@
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-immutable-radix"
|
||||
packages = ["."]
|
||||
revision = "8aac2701530899b64bdea735a1de8da899815220"
|
||||
revision = "7f3cd4390caab3250a57f30efdb2a65dd7649ecf"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/golang-lru"
|
||||
packages = ["simplelru"]
|
||||
revision = "0a025b7e63adc15a622f29b0b2c4c3848243bbf6"
|
||||
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -119,6 +169,7 @@
|
||||
".",
|
||||
"hcl/ast",
|
||||
"hcl/parser",
|
||||
"hcl/printer",
|
||||
"hcl/scanner",
|
||||
"hcl/strconv",
|
||||
"hcl/token",
|
||||
@@ -126,7 +177,7 @@
|
||||
"json/scanner",
|
||||
"json/token"
|
||||
]
|
||||
revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8"
|
||||
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
@@ -143,12 +194,6 @@
|
||||
revision = "20d3663d4bc9dd10d75abcde9d92e04b4861c674"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kardianos/osext"
|
||||
packages = ["."]
|
||||
revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kyokomi/emoji"
|
||||
packages = ["."]
|
||||
@@ -167,14 +212,13 @@
|
||||
[[projects]]
|
||||
name = "github.com/magiconair/properties"
|
||||
packages = ["."]
|
||||
revision = "be5ece7dd465ab0765a9682137865547526d1dfb"
|
||||
version = "v1.7.3"
|
||||
revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
|
||||
version = "v1.7.6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/markbates/inflect"
|
||||
packages = ["."]
|
||||
revision = "54ffb37507cd7d2ccd4768855e84f9ff6c4352b6"
|
||||
revision = "a12c3aec81a6a938bf584a4bac567afed9256586"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-runewidth"
|
||||
@@ -192,7 +236,16 @@
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
revision = "06020f85339e21b2478f756a78e295255ffa4d6a"
|
||||
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/muesli/smartcrop"
|
||||
packages = [
|
||||
".",
|
||||
"options"
|
||||
]
|
||||
revision = "f6ebaa786a12a0fdb2d7c6dee72808e68c296464"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nicksnyder/go-i18n"
|
||||
@@ -208,13 +261,13 @@
|
||||
branch = "master"
|
||||
name = "github.com/olekukonko/tablewriter"
|
||||
packages = ["."]
|
||||
revision = "65fec0d89a572b4367094e2058d3ebe667de3b60"
|
||||
revision = "b8a9be070da40449e501c3c4730a889e42d87a9e"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pelletier/go-toml"
|
||||
packages = ["."]
|
||||
revision = "16398bac157da96aa88f98a2df640c7f32af1da2"
|
||||
version = "v1.0.1"
|
||||
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
@@ -225,8 +278,14 @@
|
||||
[[projects]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
packages = ["."]
|
||||
revision = "4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c"
|
||||
version = "v1.5"
|
||||
revision = "55d61fa8aa702f59229e6cff85793c22e580eaf5"
|
||||
version = "v1.5.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sanity-io/litter"
|
||||
packages = ["."]
|
||||
revision = "ae543b7ba8fd6af63e4976198f146e1348ae53c1"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -240,23 +299,23 @@
|
||||
".",
|
||||
"mem"
|
||||
]
|
||||
revision = "ec3a3111d1e1bdff38a61e09d5a5f5e974905611"
|
||||
version = "v1.0.1"
|
||||
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cast"
|
||||
packages = ["."]
|
||||
revision = "acbeb36b902d72a7a4c18e8f3241075e7ab763e4"
|
||||
version = "v1.1.0"
|
||||
revision = "8965335b8c7107321228e3e3702cab9832751bac"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = [
|
||||
".",
|
||||
"doc"
|
||||
]
|
||||
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
|
||||
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -268,7 +327,7 @@
|
||||
branch = "master"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
packages = ["."]
|
||||
revision = "12bd96e66386c1960ab0f74ced1362f66f552f7b"
|
||||
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -285,8 +344,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/spf13/viper"
|
||||
packages = ["."]
|
||||
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
|
||||
version = "v1.0.0"
|
||||
revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
|
||||
version = "v1.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
@@ -294,8 +353,8 @@
|
||||
"assert",
|
||||
"require"
|
||||
]
|
||||
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
|
||||
version = "v1.1.4"
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/yosssi/ace"
|
||||
@@ -308,6 +367,8 @@
|
||||
name = "golang.org/x/image"
|
||||
packages = [
|
||||
"bmp",
|
||||
"draw",
|
||||
"math/f64",
|
||||
"riff",
|
||||
"tiff",
|
||||
"tiff/lzw",
|
||||
@@ -315,7 +376,7 @@
|
||||
"vp8l",
|
||||
"webp"
|
||||
]
|
||||
revision = "f7e31b4ea2e3413ab91b4e7d2dc83e5f8d19a44c"
|
||||
revision = "f315e440302883054d0c2bd85486878cb4f8572c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -324,19 +385,19 @@
|
||||
"context",
|
||||
"idna"
|
||||
]
|
||||
revision = "cd69bc3fc700721b709c3a59e16e24c67b58f6ff"
|
||||
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["errgroup"]
|
||||
revision = "fd80eb99c8f653c847d294a001bdf2a3a6f768f5"
|
||||
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "8dbc5d05d6edcc104950cc299a1ce6641235bc86"
|
||||
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -346,6 +407,8 @@
|
||||
"collate/build",
|
||||
"internal/colltab",
|
||||
"internal/gen",
|
||||
"internal/language",
|
||||
"internal/language/compact",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
@@ -358,17 +421,17 @@
|
||||
"unicode/rangetable",
|
||||
"width"
|
||||
]
|
||||
revision = "c01e4764d870b77f8abe5096ee19ad20d80e8075"
|
||||
revision = "2cb43934f0eece38629746959acc633cba083fe4"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
|
||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||
version = "v2.2.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "7259b4caf8e75db0b809f06d4897dc870261252e3aecd68ea1348c87a5da9d50"
|
||||
inputs-digest = "b9b38ece73fd6c0d0b20eb2ff85dfcc15a82841223b4403566712fd7b7ae7d59"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
+31
-17
@@ -10,19 +10,19 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/alecthomas/chroma"
|
||||
revision = "v0.2.0"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/bep/gitmap"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/chaseadamsio/goorgeous"
|
||||
revision = "v1.1.0"
|
||||
version = "^1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/disintegration/imaging"
|
||||
revision = "v1.2.4"
|
||||
version = "~v1.2.4"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/magefile/mage"
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
version = "1.4.2"
|
||||
version = "^1.4.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
@@ -52,21 +52,17 @@
|
||||
name = "github.com/jdkato/prose"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/kardianos/osext"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/kyokomi/emoji"
|
||||
version = "1.5.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/markbates/inflect"
|
||||
revision = "a12c3aec81a6a938bf584a4bac567afed9256586"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/miekg/mmark"
|
||||
version = "1.3.6"
|
||||
version = "^1.3.6"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
@@ -74,22 +70,22 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/nicksnyder/go-i18n"
|
||||
version = "1.10.0"
|
||||
version = "^1.10.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
version = "1.5.0"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/afero"
|
||||
version = "1.0.1"
|
||||
version = "^1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/cast"
|
||||
version = "1.1.0"
|
||||
version = "^1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
version = "^0.0.1"
|
||||
name = "github.com/spf13/cobra"
|
||||
|
||||
[[constraint]]
|
||||
@@ -122,7 +118,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/yosssi/ace"
|
||||
version = "0.0.5"
|
||||
version = "^0.0.5"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
@@ -135,3 +131,21 @@
|
||||
[[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"
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
-194
@@ -1,194 +0,0 @@
|
||||
Apache License
|
||||
==============
|
||||
|
||||
_Version 2.0, January 2004_
|
||||
_<<http://www.apache.org/licenses/>>_
|
||||
|
||||
### Terms and Conditions for use, reproduction, and distribution
|
||||
|
||||
#### 1. Definitions
|
||||
|
||||
“License” shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||
|
||||
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
“Source” form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
“Object” form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
“Contribution” shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
“submitted” means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as “Not a Contribution.”
|
||||
|
||||
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
#### 2. Grant of Copyright License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
#### 3. Grant of Patent License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
#### 4. Redistribution
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
#### 5. Submission of Contributions
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
#### 6. Trademarks
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
#### 7. Disclaimer of Warranty
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
#### 8. Limitation of Liability
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
#### 9. Accepting Warranty or Additional Liability
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
_END OF TERMS AND CONDITIONS_
|
||||
|
||||
### APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same “printed page” as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
@@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
A Fast and Flexible Static Site Generator built with love by [spf13](http://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go][].
|
||||
A Fast and Flexible Static Site Generator built with love by [bep](https://github.com/bep), [spf13](http://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go][].
|
||||
|
||||
[Website](https://gohugo.io) |
|
||||
[Forum](https://discourse.gohugo.io) |
|
||||
@@ -54,15 +54,28 @@ Use the [installation instructions in the Hugo documentation](https://gohugo.io/
|
||||
|
||||
### Build and Install the Binaries from Source (Advanced Install)
|
||||
|
||||
Add Hugo and its package dependencies to your go `src` directory.
|
||||
#### Prerequisite Tools
|
||||
|
||||
go get -v github.com/gohugoio/hugo
|
||||
* [Git](http://git-scm.com/)
|
||||
* [Go (latest or previous version)](https://golang.org/dl/)
|
||||
|
||||
Once the `get` completes, you should find your new `hugo` (or `hugo.exe`) executable sitting inside `$GOPATH/bin/`.
|
||||
#### Vendored Dependencies
|
||||
|
||||
To update Hugo’s dependencies, use `go get` with the `-u` option.
|
||||
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.
|
||||
|
||||
go get -u -v github.com/gohugoio/hugo
|
||||
The simplest way is to use [mage](https://github.com/magefile/mage) (a Make alternative for Go projects.)
|
||||
|
||||
#### Fetch from GitHub
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
**If you are a Windows user, substitute the `$HOME` environment variable above with `%USERPROFILE%`.**
|
||||
|
||||
## The Hugo Documentation
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ install:
|
||||
- go get github.com/magefile/mage
|
||||
|
||||
build_script:
|
||||
- mage hugoRace
|
||||
- mage vendor hugoRace
|
||||
- mage -v check
|
||||
- hugo -s docs/
|
||||
- hugo --renderToMemory -s docs/
|
||||
|
||||
+1
-1
@@ -9,4 +9,4 @@ GOEXE="${GOEXE-go}"
|
||||
|
||||
echo "Running with BenchmarkSiteBuilding/${1}"
|
||||
|
||||
"${GOEXE}" test -run="NONE" -bench="BenchmarkSiteBuilding/${1}" -test.benchmem=true ./hugolib -memprofile mem.prof -cpuprofile cpu.prof
|
||||
"${GOEXE}" test -run="NONE" -bench="BenchmarkSiteBuilding/${1}" -test.benchmem=true ./hugolib -memprofile mem.prof -count 3 -cpuprofile cpu.prof
|
||||
|
||||
+35
-27
@@ -23,47 +23,55 @@ import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var (
|
||||
type benchmarkCmd struct {
|
||||
benchmarkTimes int
|
||||
cpuProfileFile string
|
||||
memProfileFile string
|
||||
)
|
||||
|
||||
var benchmarkCmd = &cobra.Command{
|
||||
Use: "benchmark",
|
||||
Short: "Benchmark Hugo by building a site a number of times.",
|
||||
Long: `Hugo can build a site many times over and analyze the running process
|
||||
*baseBuilderCmd
|
||||
}
|
||||
|
||||
func (b *commandsBuilder) newBenchmarkCmd() *benchmarkCmd {
|
||||
cmd := &cobra.Command{
|
||||
Use: "benchmark",
|
||||
Short: "Benchmark Hugo by building a site a number of times.",
|
||||
Long: `Hugo can build a site many times over and analyze the running process
|
||||
creating a benchmark.`,
|
||||
}
|
||||
|
||||
c := &benchmarkCmd{baseBuilderCmd: b.newBuilderCmd(cmd)}
|
||||
|
||||
cmd.Flags().StringVar(&c.cpuProfileFile, "cpuprofile", "", "path/filename for the CPU profile file")
|
||||
cmd.Flags().StringVar(&c.memProfileFile, "memprofile", "", "path/filename for the memory profile file")
|
||||
cmd.Flags().IntVarP(&c.benchmarkTimes, "count", "n", 13, "number of times to build the site")
|
||||
cmd.Flags().Bool("renderToMemory", false, "render to memory (only useful for benchmark testing)")
|
||||
|
||||
cmd.RunE = c.benchmark
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func init() {
|
||||
initHugoBuilderFlags(benchmarkCmd)
|
||||
initBenchmarkBuildingFlags(benchmarkCmd)
|
||||
func (c *benchmarkCmd) benchmark(cmd *cobra.Command, args []string) error {
|
||||
cfgInit := func(c *commandeer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
benchmarkCmd.Flags().StringVar(&cpuProfileFile, "cpuprofile", "", "path/filename for the CPU profile file")
|
||||
benchmarkCmd.Flags().StringVar(&memProfileFile, "memprofile", "", "path/filename for the memory profile file")
|
||||
benchmarkCmd.Flags().IntVarP(&benchmarkTimes, "count", "n", 13, "number of times to build the site")
|
||||
|
||||
benchmarkCmd.RunE = benchmark
|
||||
}
|
||||
|
||||
func benchmark(cmd *cobra.Command, args []string) error {
|
||||
c, err := InitializeConfig(false, nil, benchmarkCmd)
|
||||
comm, err := initializeConfig(false, &c.hugoBuilderCommon, c, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var memProf *os.File
|
||||
if memProfileFile != "" {
|
||||
memProf, err = os.Create(memProfileFile)
|
||||
if c.memProfileFile != "" {
|
||||
memProf, err = os.Create(c.memProfileFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var cpuProf *os.File
|
||||
if cpuProfileFile != "" {
|
||||
cpuProf, err = os.Create(cpuProfileFile)
|
||||
if c.cpuProfileFile != "" {
|
||||
cpuProf, err = os.Create(c.cpuProfileFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -78,8 +86,8 @@ func benchmark(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
for i := 0; i < benchmarkTimes; i++ {
|
||||
if err = c.resetAndBuildSites(); err != nil {
|
||||
for i := 0; i < c.benchmarkTimes; i++ {
|
||||
if err = comm.resetAndBuildSites(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -99,9 +107,9 @@ func benchmark(cmd *cobra.Command, args []string) error {
|
||||
totalMallocs := memStats.Mallocs - mallocs
|
||||
|
||||
jww.FEEDBACK.Println()
|
||||
jww.FEEDBACK.Printf("Average time per operation: %vms\n", int(1000*totalTime.Seconds()/float64(benchmarkTimes)))
|
||||
jww.FEEDBACK.Printf("Average memory allocated per operation: %vkB\n", totalMemAllocated/uint64(benchmarkTimes)/1024)
|
||||
jww.FEEDBACK.Printf("Average allocations per operation: %v\n", totalMallocs/uint64(benchmarkTimes))
|
||||
jww.FEEDBACK.Printf("Average time per operation: %vms\n", int(1000*totalTime.Seconds()/float64(c.benchmarkTimes)))
|
||||
jww.FEEDBACK.Printf("Average memory allocated per operation: %vkB\n", totalMemAllocated/uint64(c.benchmarkTimes)/1024)
|
||||
jww.FEEDBACK.Printf("Average allocations per operation: %v\n", totalMallocs/uint64(c.benchmarkTimes))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+15
-4
@@ -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,13 +11,24 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build !darwin
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var checkCmd = &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Contains some verification checks",
|
||||
var _ cmder = (*checkCmd)(nil)
|
||||
|
||||
type checkCmd struct {
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newCheckCmd() *checkCmd {
|
||||
return &checkCmd{baseCmd: &baseCmd{cmd: &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Contains some verification checks",
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -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 commands
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var _ cmder = (*checkCmd)(nil)
|
||||
|
||||
type checkCmd struct {
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newCheckCmd() *checkCmd {
|
||||
cc := &checkCmd{baseCmd: &baseCmd{cmd: &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Contains some verification checks",
|
||||
},
|
||||
}}
|
||||
|
||||
cc.cmd.AddCommand(newLimitCmd().getCommand())
|
||||
|
||||
return cc
|
||||
}
|
||||
+182
-11
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017 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.
|
||||
@@ -14,6 +14,22 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/gohugoio/hugo/utils"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
|
||||
"github.com/bep/debounce"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
@@ -23,13 +39,31 @@ import (
|
||||
|
||||
type commandeer struct {
|
||||
*deps.DepsCfg
|
||||
|
||||
hugo *hugolib.HugoSites
|
||||
|
||||
h *hugoBuilderCommon
|
||||
ftch flagsToConfigHandler
|
||||
|
||||
pathSpec *helpers.PathSpec
|
||||
visitedURLs *types.EvictingStringQueue
|
||||
|
||||
staticDirsConfig []*src.Dirs
|
||||
|
||||
serverPorts []int
|
||||
languages helpers.Languages
|
||||
// We watch these for changes.
|
||||
configFiles []string
|
||||
|
||||
doWithCommandeer func(c *commandeer) error
|
||||
|
||||
// We can do this only once.
|
||||
fsCreate sync.Once
|
||||
|
||||
// Used in cases where we get flooded with events in server mode.
|
||||
debounce func(f func())
|
||||
|
||||
serverPorts []int
|
||||
languagesConfigured bool
|
||||
languages helpers.Languages
|
||||
|
||||
configured bool
|
||||
}
|
||||
@@ -65,16 +99,153 @@ func (c *commandeer) initFs(fs *hugofs.Fs) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newCommandeer(cfg *deps.DepsCfg, running bool) (*commandeer, error) {
|
||||
cfg.Running = running
|
||||
func newCommandeer(running bool, h *hugoBuilderCommon, f flagsToConfigHandler, doWithCommandeer func(c *commandeer) error, subCmdVs ...*cobra.Command) (*commandeer, error) {
|
||||
|
||||
var languages helpers.Languages
|
||||
|
||||
if l, ok := cfg.Cfg.Get("languagesSorted").(helpers.Languages); ok {
|
||||
languages = l
|
||||
var rebuildDebouncer func(f func())
|
||||
if running {
|
||||
// The time value used is tested with mass content replacements in a fairly big Hugo site.
|
||||
// It is better to wait for some seconds in those cases rather than get flooded
|
||||
// with rebuilds.
|
||||
rebuildDebouncer, _, _ = debounce.New(4 * time.Second)
|
||||
}
|
||||
|
||||
c := &commandeer{DepsCfg: cfg, languages: languages, visitedURLs: types.NewEvictingStringQueue(10)}
|
||||
c := &commandeer{
|
||||
h: h,
|
||||
ftch: f,
|
||||
doWithCommandeer: doWithCommandeer,
|
||||
visitedURLs: types.NewEvictingStringQueue(10),
|
||||
debounce: rebuildDebouncer,
|
||||
}
|
||||
|
||||
return c, c.loadConfig(running)
|
||||
}
|
||||
|
||||
func (c *commandeer) loadConfig(running bool) error {
|
||||
|
||||
if c.DepsCfg == nil {
|
||||
c.DepsCfg = &deps.DepsCfg{}
|
||||
}
|
||||
|
||||
cfg := c.DepsCfg
|
||||
c.configured = false
|
||||
cfg.Running = running
|
||||
|
||||
var dir string
|
||||
if c.h.source != "" {
|
||||
dir, _ = filepath.Abs(c.h.source)
|
||||
} else {
|
||||
dir, _ = os.Getwd()
|
||||
}
|
||||
|
||||
var sourceFs afero.Fs = hugofs.Os
|
||||
if c.DepsCfg.Fs != nil {
|
||||
sourceFs = c.DepsCfg.Fs.Source
|
||||
}
|
||||
|
||||
doWithConfig := func(cfg config.Provider) error {
|
||||
|
||||
if c.ftch != nil {
|
||||
c.ftch.flagsToConfig(cfg)
|
||||
}
|
||||
|
||||
cfg.Set("workingDir", dir)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
doWithCommandeer := func(cfg config.Provider) error {
|
||||
c.Cfg = cfg
|
||||
if c.doWithCommandeer == nil {
|
||||
return nil
|
||||
}
|
||||
err := c.doWithCommandeer(c)
|
||||
return err
|
||||
}
|
||||
|
||||
config, configFiles, err := hugolib.LoadConfig(
|
||||
hugolib.ConfigSourceDescriptor{Fs: sourceFs, Path: c.h.source, WorkingDir: dir, Filename: c.h.cfgFile},
|
||||
doWithCommandeer,
|
||||
doWithConfig)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.configFiles = configFiles
|
||||
|
||||
if l, ok := c.Cfg.Get("languagesSorted").(helpers.Languages); ok {
|
||||
c.languagesConfigured = true
|
||||
c.languages = l
|
||||
}
|
||||
|
||||
// This is potentially double work, but we need to do this one more time now
|
||||
// that all the languages have been configured.
|
||||
if c.doWithCommandeer != nil {
|
||||
if err := c.doWithCommandeer(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logger, err := c.createLogger(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.Logger = logger
|
||||
|
||||
createMemFs := config.GetBool("renderToMemory")
|
||||
|
||||
if createMemFs {
|
||||
// Rendering to memoryFS, publish to Root regardless of publishDir.
|
||||
config.Set("publishDir", "/")
|
||||
}
|
||||
|
||||
c.fsCreate.Do(func() {
|
||||
fs := hugofs.NewFrom(sourceFs, config)
|
||||
|
||||
// Hugo writes the output to memory instead of the disk.
|
||||
if createMemFs {
|
||||
fs.Destination = new(afero.MemMapFs)
|
||||
}
|
||||
|
||||
err = c.initFs(fs)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cacheDir := config.GetString("cacheDir")
|
||||
if cacheDir != "" {
|
||||
if helpers.FilePathSeparator != cacheDir[len(cacheDir)-1:] {
|
||||
cacheDir = cacheDir + helpers.FilePathSeparator
|
||||
}
|
||||
isDir, err := helpers.DirExists(cacheDir, sourceFs)
|
||||
utils.CheckErr(cfg.Logger, err)
|
||||
if !isDir {
|
||||
mkdir(cacheDir)
|
||||
}
|
||||
config.Set("cacheDir", cacheDir)
|
||||
} else {
|
||||
config.Set("cacheDir", helpers.GetTempDir("hugo_cache", sourceFs))
|
||||
}
|
||||
|
||||
cfg.Logger.INFO.Println("Using config file:", config.ConfigFileUsed())
|
||||
|
||||
themeDir := c.PathSpec().GetThemeDir()
|
||||
if themeDir != "" {
|
||||
if _, err := sourceFs.Stat(themeDir); os.IsNotExist(err) {
|
||||
return newSystemError("Unable to find theme Directory:", themeDir)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
// Copyright 2017 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/spf13/nitro"
|
||||
)
|
||||
|
||||
type commandsBuilder struct {
|
||||
hugoBuilderCommon
|
||||
|
||||
commands []cmder
|
||||
}
|
||||
|
||||
func newCommandsBuilder() *commandsBuilder {
|
||||
return &commandsBuilder{}
|
||||
}
|
||||
|
||||
func (b *commandsBuilder) addCommands(commands ...cmder) *commandsBuilder {
|
||||
b.commands = append(b.commands, commands...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *commandsBuilder) addAll() *commandsBuilder {
|
||||
b.addCommands(
|
||||
b.newServerCmd(),
|
||||
newVersionCmd(),
|
||||
newEnvCmd(),
|
||||
newConfigCmd(),
|
||||
newCheckCmd(),
|
||||
b.newBenchmarkCmd(),
|
||||
newConvertCmd(),
|
||||
newNewCmd(),
|
||||
newListCmd(),
|
||||
newImportCmd(),
|
||||
newGenCmd(),
|
||||
createReleaser(),
|
||||
)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *commandsBuilder) build() *hugoCmd {
|
||||
h := b.newHugoCmd()
|
||||
addCommands(h.getCommand(), b.commands...)
|
||||
return h
|
||||
}
|
||||
|
||||
func addCommands(root *cobra.Command, commands ...cmder) {
|
||||
for _, command := range commands {
|
||||
cmd := command.getCommand()
|
||||
if cmd == nil {
|
||||
continue
|
||||
}
|
||||
root.AddCommand(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
type baseCmd struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
var _ commandsBuilderGetter = (*baseBuilderCmd)(nil)
|
||||
|
||||
// Used in tests.
|
||||
type commandsBuilderGetter interface {
|
||||
getCmmandsBuilder() *commandsBuilder
|
||||
}
|
||||
type baseBuilderCmd struct {
|
||||
*baseCmd
|
||||
*commandsBuilder
|
||||
}
|
||||
|
||||
func (b *baseBuilderCmd) getCmmandsBuilder() *commandsBuilder {
|
||||
return b.commandsBuilder
|
||||
}
|
||||
|
||||
func (c *baseCmd) getCommand() *cobra.Command {
|
||||
return c.cmd
|
||||
}
|
||||
|
||||
func newBaseCmd(cmd *cobra.Command) *baseCmd {
|
||||
return &baseCmd{cmd: cmd}
|
||||
}
|
||||
|
||||
func (b *commandsBuilder) newBuilderCmd(cmd *cobra.Command) *baseBuilderCmd {
|
||||
bcmd := &baseBuilderCmd{commandsBuilder: b, baseCmd: &baseCmd{cmd: cmd}}
|
||||
bcmd.hugoBuilderCommon.handleFlags(cmd)
|
||||
return bcmd
|
||||
}
|
||||
|
||||
func (c *baseCmd) flagsToConfig(cfg config.Provider) {
|
||||
initializeFlags(c.cmd, cfg)
|
||||
}
|
||||
|
||||
type hugoCmd struct {
|
||||
*baseBuilderCmd
|
||||
|
||||
// Need to get the sites once built.
|
||||
c *commandeer
|
||||
}
|
||||
|
||||
var _ cmder = (*nilCommand)(nil)
|
||||
|
||||
type nilCommand struct {
|
||||
}
|
||||
|
||||
func (c *nilCommand) getCommand() *cobra.Command {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *nilCommand) flagsToConfig(cfg config.Provider) {
|
||||
|
||||
}
|
||||
|
||||
func (b *commandsBuilder) newHugoCmd() *hugoCmd {
|
||||
cc := &hugoCmd{}
|
||||
|
||||
cc.baseBuilderCmd = b.newBuilderCmd(&cobra.Command{
|
||||
Use: "hugo",
|
||||
Short: "hugo builds your site",
|
||||
Long: `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/.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfgInit := func(c *commandeer) error {
|
||||
if cc.buildWatch {
|
||||
c.Set("disableLiveReload", true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
c, err := initializeConfig(cc.buildWatch, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cc.c = c
|
||||
|
||||
return c.build()
|
||||
},
|
||||
})
|
||||
|
||||
cc.cmd.PersistentFlags().StringVar(&cc.cfgFile, "config", "", "config file (default is path/config.yaml|json|toml)")
|
||||
cc.cmd.PersistentFlags().BoolVar(&cc.quiet, "quiet", false, "build in quiet mode")
|
||||
|
||||
// Set bash-completion
|
||||
validConfigFilenames := []string{"json", "js", "yaml", "yml", "toml", "tml"}
|
||||
_ = cc.cmd.PersistentFlags().SetAnnotation("config", cobra.BashCompFilenameExt, validConfigFilenames)
|
||||
|
||||
cc.cmd.PersistentFlags().BoolVarP(&cc.verbose, "verbose", "v", false, "verbose output")
|
||||
cc.cmd.PersistentFlags().BoolVarP(&cc.debug, "debug", "", false, "debug output")
|
||||
cc.cmd.PersistentFlags().BoolVar(&cc.logging, "log", false, "enable Logging")
|
||||
cc.cmd.PersistentFlags().StringVar(&cc.logFile, "logFile", "", "log File path (if set, logging enabled automatically)")
|
||||
cc.cmd.PersistentFlags().BoolVar(&cc.verboseLog, "verboseLog", false, "verbose logging")
|
||||
|
||||
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)")
|
||||
|
||||
// Set bash-completion
|
||||
_ = cc.cmd.PersistentFlags().SetAnnotation("logFile", cobra.BashCompFilenameExt, []string{})
|
||||
|
||||
cc.cmd.SetGlobalNormalizationFunc(helpers.NormalizeHugoFlags)
|
||||
cc.cmd.SilenceUsage = true
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
type hugoBuilderCommon struct {
|
||||
source string
|
||||
baseURL string
|
||||
|
||||
buildWatch bool
|
||||
|
||||
gc bool
|
||||
|
||||
// TODO(bep) var vs string
|
||||
logging bool
|
||||
verbose bool
|
||||
verboseLog bool
|
||||
debug bool
|
||||
quiet bool
|
||||
|
||||
cfgFile string
|
||||
logFile string
|
||||
}
|
||||
|
||||
func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("cleanDestinationDir", false, "remove files from destination not found in static directories")
|
||||
cmd.Flags().BoolP("buildDrafts", "D", false, "include content marked as draft")
|
||||
cmd.Flags().BoolP("buildFuture", "F", false, "include content with publishdate in the future")
|
||||
cmd.Flags().BoolP("buildExpired", "E", false, "include expired content")
|
||||
cmd.Flags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from")
|
||||
cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory")
|
||||
cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory")
|
||||
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory. Defaults: $TMPDIR/hugo_cache/")
|
||||
cmd.Flags().BoolP("ignoreCache", "", false, "ignores the cache directory")
|
||||
cmd.Flags().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")
|
||||
|
||||
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")
|
||||
cmd.Flags().BoolP("i18n-warnings", "", false, "print missing translations")
|
||||
|
||||
cmd.Flags().StringSlice("disableKinds", []string{}, "disable different kind of pages (home, RSS etc.)")
|
||||
|
||||
// Set bash-completion.
|
||||
// Each flag must first be defined before using the SetAnnotation() call.
|
||||
_ = cmd.Flags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.Flags().SetAnnotation("destination", cobra.BashCompSubdirsInDir, []string{})
|
||||
_ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
// 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 commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestExecute(t *testing.T) {
|
||||
|
||||
assert := require.New(t)
|
||||
|
||||
dir, err := createSimpleTestSite(t)
|
||||
assert.NoError(err)
|
||||
|
||||
defer func() {
|
||||
os.RemoveAll(dir)
|
||||
}()
|
||||
|
||||
resp := Execute([]string{"-s=" + dir})
|
||||
assert.NoError(resp.Err)
|
||||
result := resp.Result
|
||||
assert.True(len(result.Sites) == 1)
|
||||
assert.True(len(result.Sites[0].RegularPages) == 1)
|
||||
}
|
||||
|
||||
func TestCommandsPersistentFlags(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
|
||||
noOpRunE := func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
args []string
|
||||
check func(command []cmder)
|
||||
}{{[]string{"server",
|
||||
"--config=myconfig.toml",
|
||||
"--contentDir=mycontent",
|
||||
"--disableKinds=page,home",
|
||||
"--layoutDir=mylayouts",
|
||||
"--theme=mytheme",
|
||||
"--gc",
|
||||
"--themesDir=mythemes",
|
||||
"--cleanDestinationDir",
|
||||
"--navigateToChanged",
|
||||
"--disableLiveReload",
|
||||
"--noHTTPCache",
|
||||
"--i18n-warnings",
|
||||
"--destination=/tmp/mydestination",
|
||||
"-b=https://example.com/b/",
|
||||
"--port=1366",
|
||||
"--renderToDisk",
|
||||
"--source=mysource",
|
||||
"--uglyURLs"}, func(commands []cmder) {
|
||||
var sc *serverCmd
|
||||
for _, command := range commands {
|
||||
if b, ok := command.(commandsBuilderGetter); ok {
|
||||
v := b.getCmmandsBuilder().hugoBuilderCommon
|
||||
assert.Equal("myconfig.toml", v.cfgFile)
|
||||
assert.Equal("mysource", v.source)
|
||||
assert.Equal("https://example.com/b/", v.baseURL)
|
||||
}
|
||||
|
||||
if srvCmd, ok := command.(*serverCmd); ok {
|
||||
sc = srvCmd
|
||||
}
|
||||
}
|
||||
|
||||
assert.NotNil(sc)
|
||||
assert.True(sc.navigateToChanged)
|
||||
assert.True(sc.disableLiveReload)
|
||||
assert.True(sc.noHTTPCache)
|
||||
assert.True(sc.renderToDisk)
|
||||
assert.Equal(1366, sc.serverPort)
|
||||
|
||||
cfg := viper.New()
|
||||
sc.flagsToConfig(cfg)
|
||||
assert.Equal("/tmp/mydestination", cfg.GetString("publishDir"))
|
||||
assert.Equal("mycontent", cfg.GetString("contentDir"))
|
||||
assert.Equal("mylayouts", cfg.GetString("layoutDir"))
|
||||
assert.Equal("mytheme", cfg.GetString("theme"))
|
||||
assert.Equal("mythemes", cfg.GetString("themesDir"))
|
||||
assert.Equal("https://example.com/b/", cfg.GetString("baseURL"))
|
||||
|
||||
assert.Equal([]string{"page", "home"}, cfg.Get("disableKinds"))
|
||||
|
||||
assert.True(cfg.GetBool("uglyURLs"))
|
||||
assert.True(cfg.GetBool("gc"))
|
||||
|
||||
// The flag is named i18n-warnings
|
||||
assert.True(cfg.GetBool("logI18nWarnings"))
|
||||
|
||||
}}}
|
||||
|
||||
for _, test := range tests {
|
||||
b := newCommandsBuilder()
|
||||
root := b.addAll().build()
|
||||
|
||||
for _, c := range b.commands {
|
||||
if c.getCommand() == nil {
|
||||
continue
|
||||
}
|
||||
// We are only intereseted in the flag handling here.
|
||||
c.getCommand().RunE = noOpRunE
|
||||
}
|
||||
rootCmd := root.getCommand()
|
||||
rootCmd.SetArgs(test.args)
|
||||
assert.NoError(rootCmd.Execute())
|
||||
test.check(b.commands)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCommandsExecute(t *testing.T) {
|
||||
|
||||
assert := require.New(t)
|
||||
|
||||
dir, err := createSimpleTestSite(t)
|
||||
assert.NoError(err)
|
||||
|
||||
dirOut, err := ioutil.TempDir("", "hugo-cli-out")
|
||||
assert.NoError(err)
|
||||
|
||||
defer func() {
|
||||
os.RemoveAll(dir)
|
||||
os.RemoveAll(dirOut)
|
||||
}()
|
||||
|
||||
sourceFlag := fmt.Sprintf("-s=%s", dir)
|
||||
|
||||
tests := []struct {
|
||||
commands []string
|
||||
flags []string
|
||||
expectErrToContain string
|
||||
}{
|
||||
// TODO(bep) permission issue on my OSX? "operation not permitted" {[]string{"check", "ulimit"}, nil, false},
|
||||
{[]string{"env"}, nil, ""},
|
||||
{[]string{"version"}, nil, ""},
|
||||
// no args = hugo build
|
||||
{nil, []string{sourceFlag}, ""},
|
||||
{nil, []string{sourceFlag, "--renderToMemory"}, ""},
|
||||
{[]string{"config"}, []string{sourceFlag}, ""},
|
||||
{[]string{"benchmark"}, []string{sourceFlag, "-n=1"}, ""},
|
||||
{[]string{"convert", "toTOML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "toml")}, ""},
|
||||
{[]string{"convert", "toYAML"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "yaml")}, ""},
|
||||
{[]string{"convert", "toJSON"}, []string{sourceFlag, "-o=" + filepath.Join(dirOut, "json")}, ""},
|
||||
{[]string{"gen", "autocomplete"}, []string{"--completionfile=" + filepath.Join(dirOut, "autocomplete.txt")}, ""},
|
||||
{[]string{"gen", "chromastyles"}, []string{"--style=manni"}, ""},
|
||||
{[]string{"gen", "doc"}, []string{"--dir=" + filepath.Join(dirOut, "doc")}, ""},
|
||||
{[]string{"gen", "man"}, []string{"--dir=" + filepath.Join(dirOut, "man")}, ""},
|
||||
{[]string{"list", "drafts"}, []string{sourceFlag}, ""},
|
||||
{[]string{"list", "expired"}, []string{sourceFlag}, ""},
|
||||
{[]string{"list", "future"}, []string{sourceFlag}, ""},
|
||||
{[]string{"new", "new-page.md"}, []string{sourceFlag}, ""},
|
||||
{[]string{"new", "site", filepath.Join(dirOut, "new-site")}, nil, ""},
|
||||
{[]string{"unknowncommand"}, nil, "unknown command"},
|
||||
// TODO(bep) cli refactor fix https://github.com/gohugoio/hugo/issues/4450
|
||||
//{[]string{"new", "theme", filepath.Join(dirOut, "new-theme")}, nil,false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
hugoCmd := newCommandsBuilder().addAll().build().getCommand()
|
||||
test.flags = append(test.flags, "--quiet")
|
||||
hugoCmd.SetArgs(append(test.commands, test.flags...))
|
||||
|
||||
// TODO(bep) capture output and add some simple asserts
|
||||
// TODO(bep) misspelled subcommands does not return an error. We should investigate this
|
||||
// but before that, check for "Error: unknown command".
|
||||
|
||||
_, err := hugoCmd.ExecuteC()
|
||||
if test.expectErrToContain != "" {
|
||||
assert.Error(err, fmt.Sprintf("%v", test.commands))
|
||||
assert.Contains(err.Error(), test.expectErrToContain)
|
||||
} else {
|
||||
assert.NoError(err, fmt.Sprintf("%v", test.commands))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func createSimpleTestSite(t *testing.T) (string, error) {
|
||||
d, e := ioutil.TempDir("", "hugo-cli")
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
|
||||
// Just the basic. These are for CLI tests, not site testing.
|
||||
writeFile(t, filepath.Join(d, "config.toml"), `
|
||||
|
||||
baseURL = "https://example.org"
|
||||
title = "Hugo Commands"
|
||||
|
||||
`)
|
||||
|
||||
writeFile(t, filepath.Join(d, "content", "p1.md"), `
|
||||
---
|
||||
title: "P1"
|
||||
weight: 1
|
||||
---
|
||||
|
||||
Content
|
||||
|
||||
`)
|
||||
|
||||
writeFile(t, filepath.Join(d, "layouts", "_default", "single.html"), `
|
||||
|
||||
Single: {{ .Title }}
|
||||
|
||||
`)
|
||||
|
||||
writeFile(t, filepath.Join(d, "layouts", "_default", "list.html"), `
|
||||
|
||||
List: {{ .Title }}
|
||||
|
||||
`)
|
||||
|
||||
return d, nil
|
||||
|
||||
}
|
||||
|
||||
func writeFile(t *testing.T, filename, content string) {
|
||||
must(t, os.MkdirAll(filepath.Dir(filename), os.FileMode(0755)))
|
||||
must(t, ioutil.WriteFile(filename, []byte(content), os.FileMode(0755)))
|
||||
}
|
||||
|
||||
func must(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -22,18 +22,29 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Print the site configuration",
|
||||
Long: `Print the site configuration, both default and custom settings.`,
|
||||
var _ cmder = (*configCmd)(nil)
|
||||
|
||||
type configCmd struct {
|
||||
hugoBuilderCommon
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func init() {
|
||||
configCmd.RunE = printConfig
|
||||
func newConfigCmd() *configCmd {
|
||||
cc := &configCmd{}
|
||||
cc.baseCmd = newBaseCmd(&cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Print the site configuration",
|
||||
Long: `Print the site configuration, both default and custom settings.`,
|
||||
RunE: cc.printConfig,
|
||||
})
|
||||
|
||||
cc.cmd.Flags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from")
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
func printConfig(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := InitializeConfig(false, nil, configCmd)
|
||||
func (c *configCmd) printConfig(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := initializeConfig(false, &c.hugoBuilderCommon, c, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
+62
-51
@@ -28,64 +28,75 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var outputDir string
|
||||
var unsafe bool
|
||||
var (
|
||||
_ cmder = (*convertCmd)(nil)
|
||||
)
|
||||
|
||||
var convertCmd = &cobra.Command{
|
||||
Use: "convert",
|
||||
Short: "Convert your content to different formats",
|
||||
Long: `Convert your content (e.g. front matter) to different formats.
|
||||
type convertCmd struct {
|
||||
hugoBuilderCommon
|
||||
|
||||
outputDir string
|
||||
unsafe bool
|
||||
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newConvertCmd() *convertCmd {
|
||||
cc := &convertCmd{}
|
||||
|
||||
cc.baseCmd = newBaseCmd(&cobra.Command{
|
||||
Use: "convert",
|
||||
Short: "Convert your content to different formats",
|
||||
Long: `Convert your content (e.g. front matter) to different formats.
|
||||
|
||||
See convert's subcommands toJSON, toTOML and toYAML for more information.`,
|
||||
RunE: nil,
|
||||
}
|
||||
RunE: nil,
|
||||
})
|
||||
|
||||
var toJSONCmd = &cobra.Command{
|
||||
Use: "toJSON",
|
||||
Short: "Convert front matter to JSON",
|
||||
Long: `toJSON converts all front matter in the content directory
|
||||
cc.cmd.AddCommand(
|
||||
&cobra.Command{
|
||||
Use: "toJSON",
|
||||
Short: "Convert front matter to JSON",
|
||||
Long: `toJSON converts all front matter in the content directory
|
||||
to use JSON for the front matter.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return convertContents(rune([]byte(parser.JSONLead)[0]))
|
||||
},
|
||||
}
|
||||
|
||||
var toTOMLCmd = &cobra.Command{
|
||||
Use: "toTOML",
|
||||
Short: "Convert front matter to TOML",
|
||||
Long: `toTOML converts all front matter in the content directory
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cc.convertContents(rune([]byte(parser.JSONLead)[0]))
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "toTOML",
|
||||
Short: "Convert front matter to TOML",
|
||||
Long: `toTOML converts all front matter in the content directory
|
||||
to use TOML for the front matter.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return convertContents(rune([]byte(parser.TOMLLead)[0]))
|
||||
},
|
||||
}
|
||||
|
||||
var toYAMLCmd = &cobra.Command{
|
||||
Use: "toYAML",
|
||||
Short: "Convert front matter to YAML",
|
||||
Long: `toYAML converts all front matter in the content directory
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cc.convertContents(rune([]byte(parser.TOMLLead)[0]))
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "toYAML",
|
||||
Short: "Convert front matter to YAML",
|
||||
Long: `toYAML converts all front matter in the content directory
|
||||
to use YAML for the front matter.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return convertContents(rune([]byte(parser.YAMLLead)[0]))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cc.convertContents(rune([]byte(parser.YAMLLead)[0]))
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
cc.cmd.PersistentFlags().StringVarP(&cc.outputDir, "output", "o", "", "filesystem path to write files to")
|
||||
cc.cmd.PersistentFlags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from")
|
||||
cc.cmd.PersistentFlags().BoolVar(&cc.unsafe, "unsafe", false, "enable less safe operations, please backup first")
|
||||
cc.cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
func init() {
|
||||
convertCmd.AddCommand(toJSONCmd)
|
||||
convertCmd.AddCommand(toTOMLCmd)
|
||||
convertCmd.AddCommand(toYAMLCmd)
|
||||
convertCmd.PersistentFlags().StringVarP(&outputDir, "output", "o", "", "filesystem path to write files to")
|
||||
convertCmd.PersistentFlags().StringVarP(&source, "source", "s", "", "filesystem path to read files relative from")
|
||||
convertCmd.PersistentFlags().BoolVar(&unsafe, "unsafe", false, "enable less safe operations, please backup first")
|
||||
convertCmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
}
|
||||
|
||||
func convertContents(mark rune) error {
|
||||
if outputDir == "" && !unsafe {
|
||||
func (cc *convertCmd) convertContents(mark rune) error {
|
||||
if cc.outputDir == "" && !cc.unsafe {
|
||||
return newUserError("Unsafe operation not allowed, use --unsafe or set a different output path")
|
||||
}
|
||||
|
||||
c, err := InitializeConfig(false, nil)
|
||||
c, err := initializeConfig(false, &cc.hugoBuilderCommon, cc, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -103,17 +114,17 @@ func convertContents(mark rune) error {
|
||||
|
||||
site.Log.FEEDBACK.Println("processing", len(site.AllPages), "content files")
|
||||
for _, p := range site.AllPages {
|
||||
if err := convertAndSavePage(p, site, mark); err != nil {
|
||||
if err := cc.convertAndSavePage(p, site, mark); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertAndSavePage(p *hugolib.Page, site *hugolib.Site, mark rune) error {
|
||||
func (cc *convertCmd) convertAndSavePage(p *hugolib.Page, site *hugolib.Site, mark rune) error {
|
||||
// The resources are not in .Site.AllPages.
|
||||
for _, r := range p.Resources.ByType("page") {
|
||||
if err := convertAndSavePage(r.(*hugolib.Page), site, mark); err != nil {
|
||||
if err := cc.convertAndSavePage(r.(*hugolib.Page), site, mark); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -171,8 +182,8 @@ func convertAndSavePage(p *hugolib.Page, site *hugolib.Site, mark rune) error {
|
||||
}
|
||||
|
||||
newFilename := p.Filename()
|
||||
if outputDir != "" {
|
||||
newFilename = filepath.Join(outputDir, p.Dir(), newPage.LogicalName())
|
||||
if cc.outputDir != "" {
|
||||
newFilename = filepath.Join(cc.outputDir, p.Dir(), newPage.LogicalName())
|
||||
}
|
||||
|
||||
if err = newPage.SaveSourceAs(newFilename); err != nil {
|
||||
|
||||
+20
-11
@@ -20,16 +20,25 @@ import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var envCmd = &cobra.Command{
|
||||
Use: "env",
|
||||
Short: "Print Hugo version and environment info",
|
||||
Long: `Print Hugo version and environment info. This is useful in Hugo bug reports.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
printHugoVersion()
|
||||
jww.FEEDBACK.Printf("GOOS=%q\n", runtime.GOOS)
|
||||
jww.FEEDBACK.Printf("GOARCH=%q\n", runtime.GOARCH)
|
||||
jww.FEEDBACK.Printf("GOVERSION=%q\n", runtime.Version())
|
||||
var _ cmder = (*envCmd)(nil)
|
||||
|
||||
return nil
|
||||
},
|
||||
type envCmd struct {
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newEnvCmd() *envCmd {
|
||||
return &envCmd{baseCmd: newBaseCmd(&cobra.Command{
|
||||
Use: "env",
|
||||
Short: "Print Hugo version and environment info",
|
||||
Long: `Print Hugo version and environment info. This is useful in Hugo bug reports.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
printHugoVersion()
|
||||
jww.FEEDBACK.Printf("GOOS=%q\n", runtime.GOOS)
|
||||
jww.FEEDBACK.Printf("GOARCH=%q\n", runtime.GOARCH)
|
||||
jww.FEEDBACK.Printf("GOVERSION=%q\n", runtime.Version())
|
||||
|
||||
return nil
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
+21
-3
@@ -17,7 +17,25 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var genCmd = &cobra.Command{
|
||||
Use: "gen",
|
||||
Short: "A collection of several useful generators.",
|
||||
var _ cmder = (*genCmd)(nil)
|
||||
|
||||
type genCmd struct {
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newGenCmd() *genCmd {
|
||||
cc := &genCmd{}
|
||||
cc.baseCmd = newBaseCmd(&cobra.Command{
|
||||
Use: "gen",
|
||||
Short: "A collection of several useful generators.",
|
||||
})
|
||||
|
||||
cc.cmd.AddCommand(
|
||||
newGenautocompleteCmd().getCommand(),
|
||||
newGenDocCmd().getCommand(),
|
||||
newGenManCmd().getCommand(),
|
||||
createGenDocsHelper().getCommand(),
|
||||
createGenChromaStyles().getCommand())
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
+33
-23
@@ -18,15 +18,24 @@ import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var autocompleteTarget string
|
||||
var _ cmder = (*genautocompleteCmd)(nil)
|
||||
|
||||
// bash for now (zsh and others will come)
|
||||
var autocompleteType string
|
||||
type genautocompleteCmd struct {
|
||||
autocompleteTarget string
|
||||
|
||||
var genautocompleteCmd = &cobra.Command{
|
||||
Use: "autocomplete",
|
||||
Short: "Generate shell autocompletion script for Hugo",
|
||||
Long: `Generates a shell autocompletion script for Hugo.
|
||||
// bash for now (zsh and others will come)
|
||||
autocompleteType string
|
||||
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newGenautocompleteCmd() *genautocompleteCmd {
|
||||
cc := &genautocompleteCmd{}
|
||||
|
||||
cc.baseCmd = newBaseCmd(&cobra.Command{
|
||||
Use: "autocomplete",
|
||||
Short: "Generate shell autocompletion script for Hugo",
|
||||
Long: `Generates a shell autocompletion script for Hugo.
|
||||
|
||||
NOTE: The current version supports Bash only.
|
||||
This should work for *nix systems with Bash installed.
|
||||
@@ -44,27 +53,28 @@ or just source them in directly:
|
||||
|
||||
$ . /etc/bash_completion`,
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if autocompleteType != "bash" {
|
||||
return newUserError("Only Bash is supported for now")
|
||||
}
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if cc.autocompleteType != "bash" {
|
||||
return newUserError("Only Bash is supported for now")
|
||||
}
|
||||
|
||||
err := cmd.Root().GenBashCompletionFile(autocompleteTarget)
|
||||
err := cmd.Root().GenBashCompletionFile(cc.autocompleteTarget)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jww.FEEDBACK.Println("Bash completion file for Hugo saved to", autocompleteTarget)
|
||||
jww.FEEDBACK.Println("Bash completion file for Hugo saved to", cc.autocompleteTarget)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
func init() {
|
||||
genautocompleteCmd.PersistentFlags().StringVarP(&autocompleteTarget, "completionfile", "", "/etc/bash_completion.d/hugo.sh", "autocompletion file")
|
||||
genautocompleteCmd.PersistentFlags().StringVarP(&autocompleteType, "type", "", "bash", "autocompletion type (currently only bash supported)")
|
||||
cc.cmd.PersistentFlags().StringVarP(&cc.autocompleteTarget, "completionfile", "", "/etc/bash_completion.d/hugo.sh", "autocompletion file")
|
||||
cc.cmd.PersistentFlags().StringVarP(&cc.autocompleteType, "type", "", "bash", "autocompletion type (currently only bash supported)")
|
||||
|
||||
// For bash-completion
|
||||
genautocompleteCmd.PersistentFlags().SetAnnotation("completionfile", cobra.BashCompFilenameExt, []string{})
|
||||
cc.cmd.PersistentFlags().SetAnnotation("completionfile", cobra.BashCompFilenameExt, []string{})
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
@@ -22,23 +22,27 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
_ cmder = (*genChromaStyles)(nil)
|
||||
)
|
||||
|
||||
type genChromaStyles struct {
|
||||
style string
|
||||
highlightStyle string
|
||||
linesStyle string
|
||||
cmd *cobra.Command
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
// TODO(bep) highlight
|
||||
func createGenChromaStyles() *genChromaStyles {
|
||||
g := &genChromaStyles{
|
||||
cmd: &cobra.Command{
|
||||
baseCmd: newBaseCmd(&cobra.Command{
|
||||
Use: "chromastyles",
|
||||
Short: "Generate CSS stylesheet for the Chroma code highlighter",
|
||||
Long: `Generate CSS stylesheet for the Chroma code highlighter for a given style. This stylesheet is needed if pygmentsUseClasses is enabled in config.
|
||||
|
||||
See https://help.farbox.com/pygments.html for preview of available styles`,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
g.cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
+45
-35
@@ -27,7 +27,15 @@ import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
const gendocFrontmatterTemplate = `---
|
||||
var _ cmder = (*genDocCmd)(nil)
|
||||
|
||||
type genDocCmd struct {
|
||||
gendocdir string
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newGenDocCmd() *genDocCmd {
|
||||
const gendocFrontmatterTemplate = `---
|
||||
date: %s
|
||||
title: "%s"
|
||||
slug: %s
|
||||
@@ -35,11 +43,12 @@ url: %s
|
||||
---
|
||||
`
|
||||
|
||||
var gendocdir string
|
||||
var gendocCmd = &cobra.Command{
|
||||
Use: "doc",
|
||||
Short: "Generate Markdown documentation for the Hugo CLI.",
|
||||
Long: `Generate Markdown documentation for the Hugo CLI.
|
||||
cc := &genDocCmd{}
|
||||
|
||||
cc.baseCmd = newBaseCmd(&cobra.Command{
|
||||
Use: "doc",
|
||||
Short: "Generate Markdown documentation for the Hugo CLI.",
|
||||
Long: `Generate Markdown documentation for the Hugo CLI.
|
||||
|
||||
This command is, mostly, used to create up-to-date documentation
|
||||
of Hugo's command-line interface for http://gohugo.io/.
|
||||
@@ -47,40 +56,41 @@ of Hugo's command-line interface for http://gohugo.io/.
|
||||
It creates one Markdown file per command with front matter suitable
|
||||
for rendering in Hugo.`,
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !strings.HasSuffix(gendocdir, helpers.FilePathSeparator) {
|
||||
gendocdir += helpers.FilePathSeparator
|
||||
}
|
||||
if found, _ := helpers.Exists(gendocdir, hugofs.Os); !found {
|
||||
jww.FEEDBACK.Println("Directory", gendocdir, "does not exist, creating...")
|
||||
if err := hugofs.Os.MkdirAll(gendocdir, 0777); err != nil {
|
||||
return err
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !strings.HasSuffix(cc.gendocdir, helpers.FilePathSeparator) {
|
||||
cc.gendocdir += helpers.FilePathSeparator
|
||||
}
|
||||
if found, _ := helpers.Exists(cc.gendocdir, hugofs.Os); !found {
|
||||
jww.FEEDBACK.Println("Directory", cc.gendocdir, "does not exist, creating...")
|
||||
if err := hugofs.Os.MkdirAll(cc.gendocdir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
now := time.Now().Format("2006-01-02")
|
||||
prepender := func(filename string) string {
|
||||
name := filepath.Base(filename)
|
||||
base := strings.TrimSuffix(name, path.Ext(name))
|
||||
url := "/commands/" + strings.ToLower(base) + "/"
|
||||
return fmt.Sprintf(gendocFrontmatterTemplate, now, strings.Replace(base, "_", " ", -1), base, url)
|
||||
}
|
||||
}
|
||||
now := time.Now().Format(time.RFC3339)
|
||||
prepender := func(filename string) string {
|
||||
name := filepath.Base(filename)
|
||||
base := strings.TrimSuffix(name, path.Ext(name))
|
||||
url := "/commands/" + strings.ToLower(base) + "/"
|
||||
return fmt.Sprintf(gendocFrontmatterTemplate, now, strings.Replace(base, "_", " ", -1), base, url)
|
||||
}
|
||||
|
||||
linkHandler := func(name string) string {
|
||||
base := strings.TrimSuffix(name, path.Ext(name))
|
||||
return "/commands/" + strings.ToLower(base) + "/"
|
||||
}
|
||||
linkHandler := func(name string) string {
|
||||
base := strings.TrimSuffix(name, path.Ext(name))
|
||||
return "/commands/" + strings.ToLower(base) + "/"
|
||||
}
|
||||
|
||||
jww.FEEDBACK.Println("Generating Hugo command-line documentation in", gendocdir, "...")
|
||||
doc.GenMarkdownTreeCustom(cmd.Root(), gendocdir, prepender, linkHandler)
|
||||
jww.FEEDBACK.Println("Done.")
|
||||
jww.FEEDBACK.Println("Generating Hugo command-line documentation in", cc.gendocdir, "...")
|
||||
doc.GenMarkdownTreeCustom(cmd.Root(), cc.gendocdir, prepender, linkHandler)
|
||||
jww.FEEDBACK.Println("Done.")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
func init() {
|
||||
gendocCmd.PersistentFlags().StringVar(&gendocdir, "dir", "/tmp/hugodoc/", "the directory to write the doc.")
|
||||
cc.cmd.PersistentFlags().StringVar(&cc.gendocdir, "dir", "/tmp/hugodoc/", "the directory to write the doc.")
|
||||
|
||||
// For bash-completion
|
||||
gendocCmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
|
||||
cc.cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
@@ -23,18 +23,22 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
_ cmder = (*genDocsHelper)(nil)
|
||||
)
|
||||
|
||||
type genDocsHelper struct {
|
||||
target string
|
||||
cmd *cobra.Command
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func createGenDocsHelper() *genDocsHelper {
|
||||
g := &genDocsHelper{
|
||||
cmd: &cobra.Command{
|
||||
baseCmd: newBaseCmd(&cobra.Command{
|
||||
Use: "docshelper",
|
||||
Short: "Generate some data files for the Hugo docs.",
|
||||
Hidden: true,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
g.cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
+39
-29
@@ -24,43 +24,53 @@ import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var genmandir string
|
||||
var genmanCmd = &cobra.Command{
|
||||
Use: "man",
|
||||
Short: "Generate man pages for the Hugo CLI",
|
||||
Long: `This command automatically generates up-to-date man pages of Hugo's
|
||||
var _ cmder = (*genManCmd)(nil)
|
||||
|
||||
type genManCmd struct {
|
||||
genmandir string
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newGenManCmd() *genManCmd {
|
||||
cc := &genManCmd{}
|
||||
|
||||
cc.baseCmd = newBaseCmd(&cobra.Command{
|
||||
Use: "man",
|
||||
Short: "Generate man pages for the Hugo CLI",
|
||||
Long: `This command automatically generates up-to-date man pages of Hugo's
|
||||
command-line interface. By default, it creates the man page files
|
||||
in the "man" directory under the current directory.`,
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
header := &doc.GenManHeader{
|
||||
Section: "1",
|
||||
Manual: "Hugo Manual",
|
||||
Source: fmt.Sprintf("Hugo %s", helpers.CurrentHugoVersion),
|
||||
}
|
||||
if !strings.HasSuffix(genmandir, helpers.FilePathSeparator) {
|
||||
genmandir += helpers.FilePathSeparator
|
||||
}
|
||||
if found, _ := helpers.Exists(genmandir, hugofs.Os); !found {
|
||||
jww.FEEDBACK.Println("Directory", genmandir, "does not exist, creating...")
|
||||
if err := hugofs.Os.MkdirAll(genmandir, 0777); err != nil {
|
||||
return err
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
header := &doc.GenManHeader{
|
||||
Section: "1",
|
||||
Manual: "Hugo Manual",
|
||||
Source: fmt.Sprintf("Hugo %s", helpers.CurrentHugoVersion),
|
||||
}
|
||||
}
|
||||
cmd.Root().DisableAutoGenTag = true
|
||||
if !strings.HasSuffix(cc.genmandir, helpers.FilePathSeparator) {
|
||||
cc.genmandir += helpers.FilePathSeparator
|
||||
}
|
||||
if found, _ := helpers.Exists(cc.genmandir, hugofs.Os); !found {
|
||||
jww.FEEDBACK.Println("Directory", cc.genmandir, "does not exist, creating...")
|
||||
if err := hugofs.Os.MkdirAll(cc.genmandir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmd.Root().DisableAutoGenTag = true
|
||||
|
||||
jww.FEEDBACK.Println("Generating Hugo man pages in", genmandir, "...")
|
||||
doc.GenManTree(cmd.Root(), header, genmandir)
|
||||
jww.FEEDBACK.Println("Generating Hugo man pages in", cc.genmandir, "...")
|
||||
doc.GenManTree(cmd.Root(), header, cc.genmandir)
|
||||
|
||||
jww.FEEDBACK.Println("Done.")
|
||||
jww.FEEDBACK.Println("Done.")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
func init() {
|
||||
genmanCmd.PersistentFlags().StringVar(&genmandir, "dir", "man/", "the directory to write the man pages.")
|
||||
cc.cmd.PersistentFlags().StringVar(&cc.genmandir, "dir", "man/", "the directory to write the man pages.")
|
||||
|
||||
// For bash-completion
|
||||
genmanCmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
|
||||
cc.cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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 commands defines and implements command-line commands and flags
|
||||
// used by Hugo. Commands and flags are implemented using Cobra.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
ansiEsc = "\u001B"
|
||||
clearLine = "\r\033[K"
|
||||
hideCursor = ansiEsc + "[?25l"
|
||||
showCursor = ansiEsc + "[?25h"
|
||||
)
|
||||
|
||||
type flagsToConfigHandler interface {
|
||||
flagsToConfig(cfg config.Provider)
|
||||
}
|
||||
|
||||
type cmder interface {
|
||||
flagsToConfigHandler
|
||||
getCommand() *cobra.Command
|
||||
}
|
||||
|
||||
// commandError is an error used to signal different error situations in command handling.
|
||||
type commandError struct {
|
||||
s string
|
||||
userError bool
|
||||
}
|
||||
|
||||
func (c commandError) Error() string {
|
||||
return c.s
|
||||
}
|
||||
|
||||
func (c commandError) isUserError() bool {
|
||||
return c.userError
|
||||
}
|
||||
|
||||
func newUserError(a ...interface{}) commandError {
|
||||
return commandError{s: fmt.Sprintln(a...), userError: true}
|
||||
}
|
||||
|
||||
func newSystemError(a ...interface{}) commandError {
|
||||
return commandError{s: fmt.Sprintln(a...), userError: false}
|
||||
}
|
||||
|
||||
func newSystemErrorF(format string, a ...interface{}) commandError {
|
||||
return commandError{s: fmt.Sprintf(format, a...), userError: false}
|
||||
}
|
||||
|
||||
// Catch some of the obvious user errors from Cobra.
|
||||
// We don't want to show the usage message for every error.
|
||||
// The below may be to generic. Time will show.
|
||||
var userErrorRegexp = regexp.MustCompile("argument|flag|shorthand")
|
||||
|
||||
func isUserError(err error) bool {
|
||||
if cErr, ok := err.(commandError); ok && cErr.isUserError() {
|
||||
return true
|
||||
}
|
||||
|
||||
return userErrorRegexp.MatchString(err.Error())
|
||||
}
|
||||
+239
-465
File diff suppressed because it is too large
Load Diff
+40
-39
@@ -35,33 +35,42 @@ import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
func init() {
|
||||
importCmd.AddCommand(importJekyllCmd)
|
||||
var _ cmder = (*importCmd)(nil)
|
||||
|
||||
type importCmd struct {
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
var importCmd = &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import your site from others.",
|
||||
Long: `Import your site from other web site generators like Jekyll.
|
||||
func newImportCmd() *importCmd {
|
||||
cc := &importCmd{}
|
||||
|
||||
cc.baseCmd = newBaseCmd(&cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import your site from others.",
|
||||
Long: `Import your site from other web site generators like Jekyll.
|
||||
|
||||
Import requires a subcommand, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.",
|
||||
RunE: nil,
|
||||
}
|
||||
RunE: nil,
|
||||
})
|
||||
|
||||
var importJekyllCmd = &cobra.Command{
|
||||
Use: "jekyll",
|
||||
Short: "hugo import from Jekyll",
|
||||
Long: `hugo import from Jekyll.
|
||||
importJekyllCmd := &cobra.Command{
|
||||
Use: "jekyll",
|
||||
Short: "hugo import from Jekyll",
|
||||
Long: `hugo import from Jekyll.
|
||||
|
||||
Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.",
|
||||
RunE: importFromJekyll,
|
||||
}
|
||||
RunE: cc.importFromJekyll,
|
||||
}
|
||||
|
||||
func init() {
|
||||
importJekyllCmd.Flags().Bool("force", false, "allow import into non-empty target directory")
|
||||
|
||||
cc.cmd.AddCommand(importJekyllCmd)
|
||||
|
||||
return cc
|
||||
|
||||
}
|
||||
|
||||
func importFromJekyll(cmd *cobra.Command, args []string) error {
|
||||
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`.")
|
||||
@@ -86,12 +95,12 @@ func importFromJekyll(cmd *cobra.Command, args []string) error {
|
||||
forceImport, _ := cmd.Flags().GetBool("force")
|
||||
|
||||
fs := afero.NewOsFs()
|
||||
jekyllPostDirs, hasAnyPost := getJekyllDirInfo(fs, jekyllRoot)
|
||||
jekyllPostDirs, hasAnyPost := i.getJekyllDirInfo(fs, jekyllRoot)
|
||||
if !hasAnyPost {
|
||||
return errors.New("Your Jekyll root contains neither posts nor drafts, aborting.")
|
||||
}
|
||||
|
||||
site, err := createSiteFromJekyll(jekyllRoot, targetDir, jekyllPostDirs, forceImport)
|
||||
site, err := i.createSiteFromJekyll(jekyllRoot, targetDir, jekyllPostDirs, forceImport)
|
||||
|
||||
if err != nil {
|
||||
return newUserError(err)
|
||||
@@ -144,22 +153,17 @@ func importFromJekyll(cmd *cobra.Command, args []string) error {
|
||||
"$ git clone https://github.com/spf13/herring-cove.git " + args[1] + "/themes/herring-cove")
|
||||
jww.FEEDBACK.Println("$ cd " + args[1] + "\n$ hugo server --theme=herring-cove")
|
||||
|
||||
jww.FEEDBACK.Println("Congratulations!", fileCount, "post(s) imported!")
|
||||
jww.FEEDBACK.Println("Now, start Hugo by yourself:\n" +
|
||||
"$ git clone https://github.com/spf13/herring-cove.git " + args[1] + "/themes/herring-cove")
|
||||
jww.FEEDBACK.Println("$ cd " + args[1] + "\n$ hugo server --theme=herring-cove")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getJekyllDirInfo(fs afero.Fs, jekyllRoot string) (map[string]bool, bool) {
|
||||
func (i *importCmd) getJekyllDirInfo(fs afero.Fs, jekyllRoot string) (map[string]bool, bool) {
|
||||
postDirs := make(map[string]bool)
|
||||
hasAnyPost := false
|
||||
if entries, err := ioutil.ReadDir(jekyllRoot); err == nil {
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
subDir := filepath.Join(jekyllRoot, entry.Name())
|
||||
if isPostDir, hasAnyPostInDir := retrieveJekyllPostDir(fs, subDir); isPostDir {
|
||||
if isPostDir, hasAnyPostInDir := i.retrieveJekyllPostDir(fs, subDir); isPostDir {
|
||||
postDirs[entry.Name()] = hasAnyPostInDir
|
||||
if hasAnyPostInDir {
|
||||
hasAnyPost = true
|
||||
@@ -171,7 +175,7 @@ func getJekyllDirInfo(fs afero.Fs, jekyllRoot string) (map[string]bool, bool) {
|
||||
return postDirs, hasAnyPost
|
||||
}
|
||||
|
||||
func retrieveJekyllPostDir(fs afero.Fs, dir string) (bool, bool) {
|
||||
func (i *importCmd) retrieveJekyllPostDir(fs afero.Fs, dir string) (bool, bool) {
|
||||
if strings.HasSuffix(dir, "_posts") || strings.HasSuffix(dir, "_drafts") {
|
||||
isEmpty, _ := helpers.IsEmpty(dir, fs)
|
||||
return true, !isEmpty
|
||||
@@ -181,7 +185,7 @@ func retrieveJekyllPostDir(fs afero.Fs, dir string) (bool, bool) {
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
subDir := filepath.Join(dir, entry.Name())
|
||||
if isPostDir, hasAnyPost := retrieveJekyllPostDir(fs, subDir); isPostDir {
|
||||
if isPostDir, hasAnyPost := i.retrieveJekyllPostDir(fs, subDir); isPostDir {
|
||||
return isPostDir, hasAnyPost
|
||||
}
|
||||
}
|
||||
@@ -191,7 +195,7 @@ func retrieveJekyllPostDir(fs afero.Fs, dir string) (bool, bool) {
|
||||
return false, true
|
||||
}
|
||||
|
||||
func createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[string]bool, force bool) (*hugolib.Site, error) {
|
||||
func (i *importCmd) createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[string]bool, force bool) (*hugolib.Site, error) {
|
||||
s, err := hugolib.NewSiteDefaultLang()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -210,7 +214,7 @@ func createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[strin
|
||||
}
|
||||
}
|
||||
|
||||
jekyllConfig := loadJekyllConfig(fs, jekyllRoot)
|
||||
jekyllConfig := i.loadJekyllConfig(fs, jekyllRoot)
|
||||
|
||||
mkdir(targetDir, "layouts")
|
||||
mkdir(targetDir, "content")
|
||||
@@ -219,14 +223,14 @@ func createSiteFromJekyll(jekyllRoot, targetDir string, jekyllPostDirs map[strin
|
||||
mkdir(targetDir, "data")
|
||||
mkdir(targetDir, "themes")
|
||||
|
||||
createConfigFromJekyll(fs, targetDir, "yaml", jekyllConfig)
|
||||
i.createConfigFromJekyll(fs, targetDir, "yaml", jekyllConfig)
|
||||
|
||||
copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"), jekyllPostDirs)
|
||||
i.copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"), jekyllPostDirs)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]interface{} {
|
||||
func (i *importCmd) loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]interface{} {
|
||||
path := filepath.Join(jekyllRoot, "_config.yml")
|
||||
|
||||
exists, err := helpers.Exists(path, fs)
|
||||
@@ -255,10 +259,10 @@ func loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.(map[string]interface{})
|
||||
return c
|
||||
}
|
||||
|
||||
func createConfigFromJekyll(fs afero.Fs, inpath string, kind string, jekyllConfig map[string]interface{}) (err error) {
|
||||
func (i *importCmd) createConfigFromJekyll(fs afero.Fs, inpath string, kind string, jekyllConfig map[string]interface{}) (err error) {
|
||||
title := "My New Hugo Site"
|
||||
baseURL := "http://example.org/"
|
||||
|
||||
@@ -353,7 +357,7 @@ func copyDir(source string, dest string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyllPostDirs map[string]bool) (err error) {
|
||||
func (i *importCmd) copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyllPostDirs map[string]bool) (err error) {
|
||||
fi, err := os.Stat(jekyllRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -478,8 +482,6 @@ func convertJekyllPost(s *hugolib.Site, path, relPath, targetDir string, draft b
|
||||
}
|
||||
|
||||
func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, draft bool) (interface{}, error) {
|
||||
url := postDate.Format("/2006/01/02/") + postName + "/"
|
||||
|
||||
metadata, err := cast.ToStringMapE(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -497,7 +499,7 @@ func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, d
|
||||
delete(metadata, key)
|
||||
case "permalink":
|
||||
if str, ok := value.(string); ok {
|
||||
url = str
|
||||
metadata["url"] = str
|
||||
}
|
||||
delete(metadata, key)
|
||||
case "category":
|
||||
@@ -526,7 +528,6 @@ func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, d
|
||||
|
||||
}
|
||||
|
||||
metadata["url"] = url
|
||||
metadata["date"] = postDate.Format(time.RFC3339)
|
||||
|
||||
return metadata, nil
|
||||
|
||||
@@ -51,9 +51,9 @@ func TestConvertJekyllMetadata(t *testing.T) {
|
||||
expect string
|
||||
}{
|
||||
{map[interface{}]interface{}{}, "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
|
||||
`{"date":"2015-10-01T00:00:00Z","url":"/2015/10/01/testPost/"}`},
|
||||
`{"date":"2015-10-01T00:00:00Z"}`},
|
||||
{map[interface{}]interface{}{}, "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), true,
|
||||
`{"date":"2015-10-01T00:00:00Z","draft":true,"url":"/2015/10/01/testPost/"}`},
|
||||
`{"date":"2015-10-01T00:00:00Z","draft":true}`},
|
||||
{map[interface{}]interface{}{"Permalink": "/permalink.html", "layout": "post"},
|
||||
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
|
||||
`{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`},
|
||||
@@ -62,13 +62,13 @@ func TestConvertJekyllMetadata(t *testing.T) {
|
||||
`{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`},
|
||||
{map[interface{}]interface{}{"category": nil, "permalink": 123},
|
||||
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
|
||||
`{"date":"2015-10-01T00:00:00Z","url":"/2015/10/01/testPost/"}`},
|
||||
`{"date":"2015-10-01T00:00:00Z"}`},
|
||||
{map[interface{}]interface{}{"Excerpt_Separator": "sep"},
|
||||
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
|
||||
`{"date":"2015-10-01T00:00:00Z","excerpt_separator":"sep","url":"/2015/10/01/testPost/"}`},
|
||||
`{"date":"2015-10-01T00:00:00Z","excerpt_separator":"sep"}`},
|
||||
{map[interface{}]interface{}{"category": "book", "layout": "post", "Others": "Goods", "Date": "2015-10-01 12:13:11"},
|
||||
"testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
|
||||
`{"Others":"Goods","categories":["book"],"date":"2015-10-01T12:13:11Z","url":"/2015/10/01/testPost/"}`},
|
||||
`{"Others":"Goods","categories":["book"],"date":"2015-10-01T12:13:11Z"}`},
|
||||
}
|
||||
|
||||
for _, data := range testDataList {
|
||||
|
||||
+34
-41
@@ -1,17 +1,4 @@
|
||||
// Copyright 2015 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// 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.
|
||||
@@ -33,39 +20,45 @@ import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
func init() {
|
||||
checkCmd.AddCommand(limit)
|
||||
var _ cmder = (*limitCmd)(nil)
|
||||
|
||||
type limitCmd struct {
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
var limit = &cobra.Command{
|
||||
Use: "ulimit",
|
||||
Short: "Check system ulimit settings",
|
||||
Long: `Hugo will inspect the current ulimit settings on the system.
|
||||
func newLimitCmd() *limitCmd {
|
||||
ccmd := &cobra.Command{
|
||||
Use: "ulimit",
|
||||
Short: "Check system ulimit settings",
|
||||
Long: `Hugo will inspect the current ulimit settings on the system.
|
||||
This is primarily to ensure that Hugo can watch enough files on some OSs`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var rLimit syscall.Rlimit
|
||||
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
return newSystemError("Error Getting Rlimit ", err)
|
||||
}
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var rLimit syscall.Rlimit
|
||||
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
return newSystemError("Error Getting Rlimit ", err)
|
||||
}
|
||||
|
||||
jww.FEEDBACK.Println("Current rLimit:", rLimit)
|
||||
jww.FEEDBACK.Println("Current rLimit:", rLimit)
|
||||
|
||||
jww.FEEDBACK.Println("Attempting to increase limit")
|
||||
rLimit.Max = 999999
|
||||
rLimit.Cur = 999999
|
||||
err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
return newSystemError("Error Setting rLimit ", err)
|
||||
}
|
||||
err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
return newSystemError("Error Getting rLimit ", err)
|
||||
}
|
||||
jww.FEEDBACK.Println("rLimit after change:", rLimit)
|
||||
jww.FEEDBACK.Println("Attempting to increase limit")
|
||||
rLimit.Max = 999999
|
||||
rLimit.Cur = 999999
|
||||
err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
return newSystemError("Error Setting rLimit ", err)
|
||||
}
|
||||
err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
return newSystemError("Error Getting rLimit ", err)
|
||||
}
|
||||
jww.FEEDBACK.Println("rLimit after change:", rLimit)
|
||||
|
||||
return nil
|
||||
},
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return &limitCmd{baseCmd: newBaseCmd(ccmd)}
|
||||
}
|
||||
|
||||
func tweakLimit() {
|
||||
|
||||
@@ -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.
|
||||
@@ -12,18 +12,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
// +build !darwin
|
||||
// Copyright 2015 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package commands
|
||||
|
||||
|
||||
+105
-97
@@ -21,129 +21,137 @@ import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
func init() {
|
||||
listCmd.AddCommand(listDraftsCmd)
|
||||
listCmd.AddCommand(listFutureCmd)
|
||||
listCmd.AddCommand(listExpiredCmd)
|
||||
listCmd.PersistentFlags().StringVarP(&source, "source", "s", "", "filesystem path to read files relative from")
|
||||
listCmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
var _ cmder = (*listCmd)(nil)
|
||||
|
||||
type listCmd struct {
|
||||
hugoBuilderCommon
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "Listing out various types of content",
|
||||
Long: `Listing out various types of content.
|
||||
func newListCmd() *listCmd {
|
||||
cc := &listCmd{}
|
||||
|
||||
cc.baseCmd = newBaseCmd(&cobra.Command{
|
||||
Use: "list",
|
||||
Short: "Listing out various types of content",
|
||||
Long: `Listing out various types of content.
|
||||
|
||||
List requires a subcommand, e.g. ` + "`hugo list drafts`.",
|
||||
RunE: nil,
|
||||
}
|
||||
RunE: nil,
|
||||
})
|
||||
|
||||
var listDraftsCmd = &cobra.Command{
|
||||
Use: "drafts",
|
||||
Short: "List all drafts",
|
||||
Long: `List all of the drafts in your content directory.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfgInit := func(c *commandeer) error {
|
||||
c.Set("buildDrafts", true)
|
||||
return nil
|
||||
}
|
||||
c, err := InitializeConfig(false, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cc.cmd.AddCommand(
|
||||
&cobra.Command{
|
||||
Use: "drafts",
|
||||
Short: "List all drafts",
|
||||
Long: `List all of the drafts in your content directory.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfgInit := func(c *commandeer) error {
|
||||
c.Set("buildDrafts", true)
|
||||
return nil
|
||||
}
|
||||
c, err := initializeConfig(false, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sites, err := hugolib.NewHugoSites(*c.DepsCfg)
|
||||
sites, err := hugolib.NewHugoSites(*c.DepsCfg)
|
||||
|
||||
if err != nil {
|
||||
return newSystemError("Error creating sites", err)
|
||||
}
|
||||
if err != nil {
|
||||
return newSystemError("Error creating sites", err)
|
||||
}
|
||||
|
||||
if err := sites.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return newSystemError("Error Processing Source Content", err)
|
||||
}
|
||||
if err := sites.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return newSystemError("Error Processing Source Content", err)
|
||||
}
|
||||
|
||||
for _, p := range sites.Pages() {
|
||||
if p.IsDraft() {
|
||||
jww.FEEDBACK.Println(filepath.Join(p.File.Dir(), p.File.LogicalName()))
|
||||
}
|
||||
for _, p := range sites.Pages() {
|
||||
if p.IsDraft() {
|
||||
jww.FEEDBACK.Println(filepath.Join(p.File.Dir(), p.File.LogicalName()))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
var listFutureCmd = &cobra.Command{
|
||||
Use: "future",
|
||||
Short: "List all posts dated in the future",
|
||||
Long: `List all of the posts in your content directory which will be
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "future",
|
||||
Short: "List all posts dated in the future",
|
||||
Long: `List all of the posts in your content directory which will be
|
||||
posted in the future.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfgInit := func(c *commandeer) error {
|
||||
c.Set("buildFuture", true)
|
||||
return nil
|
||||
}
|
||||
c, err := InitializeConfig(false, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfgInit := func(c *commandeer) error {
|
||||
c.Set("buildFuture", true)
|
||||
return nil
|
||||
}
|
||||
c, err := initializeConfig(false, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sites, err := hugolib.NewHugoSites(*c.DepsCfg)
|
||||
sites, err := hugolib.NewHugoSites(*c.DepsCfg)
|
||||
|
||||
if err != nil {
|
||||
return newSystemError("Error creating sites", err)
|
||||
}
|
||||
if err != nil {
|
||||
return newSystemError("Error creating sites", err)
|
||||
}
|
||||
|
||||
if err := sites.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return newSystemError("Error Processing Source Content", err)
|
||||
}
|
||||
if err := sites.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return newSystemError("Error Processing Source Content", err)
|
||||
}
|
||||
|
||||
for _, p := range sites.Pages() {
|
||||
if p.IsFuture() {
|
||||
jww.FEEDBACK.Println(filepath.Join(p.File.Dir(), p.File.LogicalName()))
|
||||
}
|
||||
for _, p := range sites.Pages() {
|
||||
if p.IsFuture() {
|
||||
jww.FEEDBACK.Println(filepath.Join(p.File.Dir(), p.File.LogicalName()))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
var listExpiredCmd = &cobra.Command{
|
||||
Use: "expired",
|
||||
Short: "List all posts already expired",
|
||||
Long: `List all of the posts in your content directory which has already
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "expired",
|
||||
Short: "List all posts already expired",
|
||||
Long: `List all of the posts in your content directory which has already
|
||||
expired.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfgInit := func(c *commandeer) error {
|
||||
c.Set("buildExpired", true)
|
||||
return nil
|
||||
}
|
||||
c, err := InitializeConfig(false, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfgInit := func(c *commandeer) error {
|
||||
c.Set("buildExpired", true)
|
||||
return nil
|
||||
}
|
||||
c, err := initializeConfig(false, &cc.hugoBuilderCommon, cc, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sites, err := hugolib.NewHugoSites(*c.DepsCfg)
|
||||
sites, err := hugolib.NewHugoSites(*c.DepsCfg)
|
||||
|
||||
if err != nil {
|
||||
return newSystemError("Error creating sites", err)
|
||||
}
|
||||
if err != nil {
|
||||
return newSystemError("Error creating sites", err)
|
||||
}
|
||||
|
||||
if err := sites.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return newSystemError("Error Processing Source Content", err)
|
||||
}
|
||||
if err := sites.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return newSystemError("Error Processing Source Content", err)
|
||||
}
|
||||
|
||||
for _, p := range sites.Pages() {
|
||||
if p.IsExpired() {
|
||||
jww.FEEDBACK.Println(filepath.Join(p.File.Dir(), p.File.LogicalName()))
|
||||
}
|
||||
for _, p := range sites.Pages() {
|
||||
if p.IsExpired() {
|
||||
jww.FEEDBACK.Println(filepath.Join(p.File.Dir(), p.File.LogicalName()))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
cc.cmd.PersistentFlags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from")
|
||||
cc.cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
+33
-247
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 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.
|
||||
@@ -15,85 +15,63 @@ package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/create"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
configFormat string
|
||||
var _ cmder = (*newCmd)(nil)
|
||||
|
||||
type newCmd struct {
|
||||
hugoBuilderCommon
|
||||
contentEditor string
|
||||
contentType string
|
||||
)
|
||||
|
||||
func init() {
|
||||
newSiteCmd.Flags().StringVarP(&configFormat, "format", "f", "toml", "config & frontmatter format")
|
||||
newSiteCmd.Flags().Bool("force", false, "init inside non-empty directory")
|
||||
newCmd.Flags().StringVarP(&contentType, "kind", "k", "", "content type to create")
|
||||
newCmd.PersistentFlags().StringVarP(&source, "source", "s", "", "filesystem path to read files relative from")
|
||||
newCmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
newCmd.Flags().StringVar(&contentEditor, "editor", "", "edit new content with this editor, if provided")
|
||||
|
||||
newCmd.AddCommand(newSiteCmd)
|
||||
newCmd.AddCommand(newThemeCmd)
|
||||
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
var newCmd = &cobra.Command{
|
||||
Use: "new [path]",
|
||||
Short: "Create new content for your site",
|
||||
Long: `Create a new content file and automatically set the date and title.
|
||||
func newNewCmd() *newCmd {
|
||||
cc := &newCmd{}
|
||||
cc.baseCmd = newBaseCmd(&cobra.Command{
|
||||
Use: "new [path]",
|
||||
Short: "Create new content for your site",
|
||||
Long: `Create a new content file and automatically set the date and title.
|
||||
It will guess which kind of file to create based on the path provided.
|
||||
|
||||
You can also specify the kind with ` + "`-k KIND`" + `.
|
||||
|
||||
If archetypes are provided in your theme or site, they will be used.`,
|
||||
|
||||
RunE: NewContent,
|
||||
RunE: cc.newContent,
|
||||
})
|
||||
|
||||
cc.cmd.Flags().StringVarP(&cc.contentType, "kind", "k", "", "content type to create")
|
||||
cc.cmd.PersistentFlags().StringVarP(&cc.source, "source", "s", "", "filesystem path to read files relative from")
|
||||
cc.cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||
cc.cmd.Flags().StringVar(&cc.contentEditor, "editor", "", "edit new content with this editor, if provided")
|
||||
|
||||
cc.cmd.AddCommand(newNewSiteCmd().getCommand())
|
||||
cc.cmd.AddCommand(newNewThemeCmd().getCommand())
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
var newSiteCmd = &cobra.Command{
|
||||
Use: "site [path]",
|
||||
Short: "Create a new site (skeleton)",
|
||||
Long: `Create a new site in the provided directory.
|
||||
The new site will have the correct structure, but no content or theme yet.
|
||||
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
RunE: NewSite,
|
||||
}
|
||||
|
||||
var newThemeCmd = &cobra.Command{
|
||||
Use: "theme [name]",
|
||||
Short: "Create a new theme",
|
||||
Long: `Create a new theme (skeleton) called [name] in the current directory.
|
||||
New theme is a skeleton. Please add content to the touched files. Add your
|
||||
name to the copyright line in the license and adjust the theme.toml file
|
||||
as you see fit.`,
|
||||
RunE: NewTheme,
|
||||
}
|
||||
|
||||
// NewContent adds new content to a Hugo site.
|
||||
func NewContent(cmd *cobra.Command, args []string) error {
|
||||
func (n *newCmd) newContent(cmd *cobra.Command, args []string) error {
|
||||
cfgInit := func(c *commandeer) error {
|
||||
if cmd.Flags().Changed("editor") {
|
||||
c.Set("newContentEditor", contentEditor)
|
||||
c.Set("newContentEditor", n.contentEditor)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
c, err := InitializeConfig(false, cfgInit)
|
||||
c, err := initializeConfig(false, &n.hugoBuilderCommon, n, cfgInit)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -109,8 +87,8 @@ func NewContent(cmd *cobra.Command, args []string) error {
|
||||
|
||||
createPath, kind = newContentPathSection(createPath)
|
||||
|
||||
if contentType != "" {
|
||||
kind = contentType
|
||||
if n.contentType != "" {
|
||||
kind = n.contentType
|
||||
}
|
||||
|
||||
cfg := c.DepsCfg
|
||||
@@ -130,15 +108,15 @@ func NewContent(cmd *cobra.Command, args []string) error {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := Hugo.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
if err := c.hugo.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s = Hugo.Sites[0]
|
||||
s = c.hugo.Sites[0]
|
||||
|
||||
if len(Hugo.Sites) > 1 {
|
||||
if len(c.hugo.Sites) > 1 {
|
||||
// Find the best match.
|
||||
for _, ss := range Hugo.Sites {
|
||||
for _, ss := range c.hugo.Sites {
|
||||
if strings.Contains(createPath, "."+ss.Language.Lang) {
|
||||
s = ss
|
||||
break
|
||||
@@ -151,56 +129,6 @@ func NewContent(cmd *cobra.Command, args []string) error {
|
||||
return create.NewContent(ps, siteFactory, kind, createPath)
|
||||
}
|
||||
|
||||
func doNewSite(fs *hugofs.Fs, basepath string, force bool) error {
|
||||
archeTypePath := filepath.Join(basepath, "archetypes")
|
||||
dirs := []string{
|
||||
filepath.Join(basepath, "layouts"),
|
||||
filepath.Join(basepath, "content"),
|
||||
archeTypePath,
|
||||
filepath.Join(basepath, "static"),
|
||||
filepath.Join(basepath, "data"),
|
||||
filepath.Join(basepath, "themes"),
|
||||
}
|
||||
|
||||
if exists, _ := helpers.Exists(basepath, fs.Source); exists {
|
||||
if isDir, _ := helpers.IsDir(basepath, fs.Source); !isDir {
|
||||
return errors.New(basepath + " already exists but not a directory")
|
||||
}
|
||||
|
||||
isEmpty, _ := helpers.IsEmpty(basepath, fs.Source)
|
||||
|
||||
switch {
|
||||
case !isEmpty && !force:
|
||||
return errors.New(basepath + " already exists and is not empty")
|
||||
|
||||
case !isEmpty && force:
|
||||
all := append(dirs, filepath.Join(basepath, "config."+configFormat))
|
||||
for _, path := range all {
|
||||
if exists, _ := helpers.Exists(path, fs.Source); exists {
|
||||
return errors.New(path + " already exists")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
if err := fs.Source.MkdirAll(dir, 0777); err != nil {
|
||||
return fmt.Errorf("Failed to create dir: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
createConfig(fs, basepath, configFormat)
|
||||
|
||||
// Create a defaul archetype file.
|
||||
helpers.SafeWriteToDisk(filepath.Join(archeTypePath, "default.md"),
|
||||
strings.NewReader(create.ArchetypeTemplateTemplate), fs.Source)
|
||||
|
||||
jww.FEEDBACK.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", basepath)
|
||||
jww.FEEDBACK.Println(nextStepsText())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func nextStepsText() string {
|
||||
var nextStepsText bytes.Buffer
|
||||
|
||||
@@ -222,98 +150,6 @@ Visit https://gohugo.io/ for quickstart guide and full documentation.`)
|
||||
return nextStepsText.String()
|
||||
}
|
||||
|
||||
// NewSite creates a new Hugo site and initializes a structured Hugo directory.
|
||||
func NewSite(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return newUserError("path needs to be provided")
|
||||
}
|
||||
|
||||
createpath, err := filepath.Abs(filepath.Clean(args[0]))
|
||||
if err != nil {
|
||||
return newUserError(err)
|
||||
}
|
||||
|
||||
forceNew, _ := cmd.Flags().GetBool("force")
|
||||
|
||||
return doNewSite(hugofs.NewDefault(viper.New()), createpath, forceNew)
|
||||
}
|
||||
|
||||
// NewTheme creates a new Hugo theme.
|
||||
func NewTheme(cmd *cobra.Command, args []string) error {
|
||||
c, err := InitializeConfig(false, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
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)
|
||||
|
||||
cfg := c.DepsCfg
|
||||
|
||||
if x, _ := helpers.Exists(createpath, cfg.Fs.Source); x {
|
||||
return newUserError(createpath, "already exists")
|
||||
}
|
||||
|
||||
mkdir(createpath, "layouts", "_default")
|
||||
mkdir(createpath, "layouts", "partials")
|
||||
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "index.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "404.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "list.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "single.html")
|
||||
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "header.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "footer.html")
|
||||
|
||||
mkdir(createpath, "archetypes")
|
||||
|
||||
archDefault := []byte("+++\n+++\n")
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), cfg.Fs.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mkdir(createpath, "static", "js")
|
||||
mkdir(createpath, "static", "css")
|
||||
|
||||
by := []byte(`The MIT License (MIT)
|
||||
|
||||
Copyright (c) ` + time.Now().Format("2006") + ` YOUR_NAME_HERE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), cfg.Fs.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
createThemeMD(cfg.Fs, createpath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mkdir(x ...string) {
|
||||
p := filepath.Join(x...)
|
||||
|
||||
@@ -332,39 +168,6 @@ func touchFile(fs afero.Fs, x ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
func createThemeMD(fs *hugofs.Fs, inpath string) (err error) {
|
||||
|
||||
by := []byte(`# theme.toml template for a Hugo theme
|
||||
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
|
||||
|
||||
name = "` + strings.Title(helpers.MakeTitle(filepath.Base(inpath))) + `"
|
||||
license = "MIT"
|
||||
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE.md"
|
||||
description = ""
|
||||
homepage = "http://example.com/"
|
||||
tags = []
|
||||
features = []
|
||||
min_version = "0.32"
|
||||
|
||||
[author]
|
||||
name = ""
|
||||
homepage = ""
|
||||
|
||||
# If porting an existing theme
|
||||
[original]
|
||||
name = ""
|
||||
homepage = ""
|
||||
repo = ""
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), fs.Source)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newContentPathSection(path string) (string, string) {
|
||||
// Forward slashes is used in all examples. Convert if needed.
|
||||
// Issue #1133
|
||||
@@ -381,20 +184,3 @@ func newContentPathSection(path string) (string, string) {
|
||||
|
||||
return createpath, section
|
||||
}
|
||||
|
||||
func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
|
||||
in := map[string]string{
|
||||
"baseURL": "http://example.org/",
|
||||
"title": "My New Hugo Site",
|
||||
"languageCode": "en-us",
|
||||
}
|
||||
kind = parser.FormatSanitize(kind)
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind), &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), &buf, fs.Source)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 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.
|
||||
@@ -48,10 +48,11 @@ func checkNewSiteInited(fs *hugofs.Fs, basepath string, t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDoNewSite(t *testing.T) {
|
||||
n := newNewSiteCmd()
|
||||
basepath := filepath.Join("base", "blog")
|
||||
_, fs := newTestCfg()
|
||||
|
||||
require.NoError(t, doNewSite(fs, basepath, false))
|
||||
require.NoError(t, n.doNewSite(fs, basepath, false))
|
||||
|
||||
checkNewSiteInited(fs, basepath, t)
|
||||
}
|
||||
@@ -59,31 +60,34 @@ func TestDoNewSite(t *testing.T) {
|
||||
func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
|
||||
basepath := filepath.Join("base", "blog")
|
||||
_, fs := newTestCfg()
|
||||
n := newNewSiteCmd()
|
||||
|
||||
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
||||
|
||||
require.NoError(t, doNewSite(fs, basepath, false))
|
||||
require.NoError(t, n.doNewSite(fs, basepath, false))
|
||||
}
|
||||
|
||||
func TestDoNewSite_error_base_exists(t *testing.T) {
|
||||
basepath := filepath.Join("base", "blog")
|
||||
_, fs := newTestCfg()
|
||||
n := newNewSiteCmd()
|
||||
|
||||
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
||||
_, err := fs.Source.Create(filepath.Join(basepath, "foo"))
|
||||
require.NoError(t, err)
|
||||
// Since the directory already exists and isn't empty, expect an error
|
||||
require.Error(t, doNewSite(fs, basepath, false))
|
||||
require.Error(t, n.doNewSite(fs, basepath, false))
|
||||
|
||||
}
|
||||
|
||||
func TestDoNewSite_force_empty_dir(t *testing.T) {
|
||||
basepath := filepath.Join("base", "blog")
|
||||
_, fs := newTestCfg()
|
||||
n := newNewSiteCmd()
|
||||
|
||||
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
||||
|
||||
require.NoError(t, doNewSite(fs, basepath, true))
|
||||
require.NoError(t, n.doNewSite(fs, basepath, true))
|
||||
|
||||
checkNewSiteInited(fs, basepath, t)
|
||||
}
|
||||
@@ -91,23 +95,25 @@ func TestDoNewSite_force_empty_dir(t *testing.T) {
|
||||
func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) {
|
||||
basepath := filepath.Join("base", "blog")
|
||||
_, fs := newTestCfg()
|
||||
n := newNewSiteCmd()
|
||||
|
||||
contentPath := filepath.Join(basepath, "content")
|
||||
|
||||
require.NoError(t, fs.Source.MkdirAll(contentPath, 777))
|
||||
require.Error(t, doNewSite(fs, basepath, true))
|
||||
require.Error(t, n.doNewSite(fs, basepath, true))
|
||||
}
|
||||
|
||||
func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
|
||||
basepath := filepath.Join("base", "blog")
|
||||
_, fs := newTestCfg()
|
||||
n := newNewSiteCmd()
|
||||
|
||||
configPath := filepath.Join(basepath, "config.toml")
|
||||
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
||||
_, err := fs.Source.Create(configPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Error(t, doNewSite(fs, basepath, true))
|
||||
require.Error(t, n.doNewSite(fs, basepath, true))
|
||||
}
|
||||
|
||||
func newTestCfg() (*viper.Viper, *hugofs.Fs) {
|
||||
@@ -0,0 +1,148 @@
|
||||
// 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 commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/create"
|
||||
|
||||
"fmt"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/spf13/cobra"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var _ cmder = (*newSiteCmd)(nil)
|
||||
|
||||
type newSiteCmd struct {
|
||||
configFormat string
|
||||
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newNewSiteCmd() *newSiteCmd {
|
||||
ccmd := &newSiteCmd{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "site [path]",
|
||||
Short: "Create a new site (skeleton)",
|
||||
Long: `Create a new site in the provided directory.
|
||||
The new site will have the correct structure, but no content or theme yet.
|
||||
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
RunE: ccmd.newSite,
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&ccmd.configFormat, "format", "f", "toml", "config & frontmatter format")
|
||||
cmd.Flags().Bool("force", false, "init inside non-empty directory")
|
||||
|
||||
ccmd.baseCmd = newBaseCmd(cmd)
|
||||
|
||||
return ccmd
|
||||
|
||||
}
|
||||
|
||||
func (n *newSiteCmd) doNewSite(fs *hugofs.Fs, basepath string, force bool) error {
|
||||
archeTypePath := filepath.Join(basepath, "archetypes")
|
||||
dirs := []string{
|
||||
filepath.Join(basepath, "layouts"),
|
||||
filepath.Join(basepath, "content"),
|
||||
archeTypePath,
|
||||
filepath.Join(basepath, "static"),
|
||||
filepath.Join(basepath, "data"),
|
||||
filepath.Join(basepath, "themes"),
|
||||
}
|
||||
|
||||
if exists, _ := helpers.Exists(basepath, fs.Source); exists {
|
||||
if isDir, _ := helpers.IsDir(basepath, fs.Source); !isDir {
|
||||
return errors.New(basepath + " already exists but not a directory")
|
||||
}
|
||||
|
||||
isEmpty, _ := helpers.IsEmpty(basepath, fs.Source)
|
||||
|
||||
switch {
|
||||
case !isEmpty && !force:
|
||||
return errors.New(basepath + " already exists and is not empty")
|
||||
|
||||
case !isEmpty && force:
|
||||
all := append(dirs, filepath.Join(basepath, "config."+n.configFormat))
|
||||
for _, path := range all {
|
||||
if exists, _ := helpers.Exists(path, fs.Source); exists {
|
||||
return errors.New(path + " already exists")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
if err := fs.Source.MkdirAll(dir, 0777); err != nil {
|
||||
return fmt.Errorf("Failed to create dir: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
createConfig(fs, basepath, n.configFormat)
|
||||
|
||||
// Create a defaul archetype file.
|
||||
helpers.SafeWriteToDisk(filepath.Join(archeTypePath, "default.md"),
|
||||
strings.NewReader(create.ArchetypeTemplateTemplate), fs.Source)
|
||||
|
||||
jww.FEEDBACK.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", basepath)
|
||||
jww.FEEDBACK.Println(nextStepsText())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newSite creates a new Hugo site and initializes a structured Hugo directory.
|
||||
func (n *newSiteCmd) newSite(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return newUserError("path needs to be provided")
|
||||
}
|
||||
|
||||
createpath, err := filepath.Abs(filepath.Clean(args[0]))
|
||||
if err != nil {
|
||||
return newUserError(err)
|
||||
}
|
||||
|
||||
forceNew, _ := cmd.Flags().GetBool("force")
|
||||
|
||||
return n.doNewSite(hugofs.NewDefault(viper.New()), createpath, forceNew)
|
||||
}
|
||||
|
||||
func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
|
||||
in := map[string]string{
|
||||
"baseURL": "http://example.org/",
|
||||
"title": "My New Hugo Site",
|
||||
"languageCode": "en-us",
|
||||
}
|
||||
kind = parser.FormatSanitize(kind)
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind), &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), &buf, fs.Source)
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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 commands
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"bytes"
|
||||
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/spf13/cobra"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var _ cmder = (*newThemeCmd)(nil)
|
||||
|
||||
type newThemeCmd struct {
|
||||
*baseCmd
|
||||
hugoBuilderCommon
|
||||
}
|
||||
|
||||
func newNewThemeCmd() *newThemeCmd {
|
||||
ccmd := &newThemeCmd{baseCmd: newBaseCmd(nil)}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "theme [name]",
|
||||
Short: "Create a new theme",
|
||||
Long: `Create a new theme (skeleton) called [name] in the current directory.
|
||||
New theme is a skeleton. Please add content to the touched files. Add your
|
||||
name to the copyright line in the license and adjust the theme.toml file
|
||||
as you see fit.`,
|
||||
RunE: ccmd.newTheme,
|
||||
}
|
||||
|
||||
ccmd.cmd = cmd
|
||||
|
||||
return ccmd
|
||||
}
|
||||
|
||||
func (n *newThemeCmd) newTheme(cmd *cobra.Command, args []string) error {
|
||||
c, err := initializeConfig(false, &n.hugoBuilderCommon, n, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
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)
|
||||
|
||||
cfg := c.DepsCfg
|
||||
|
||||
if x, _ := helpers.Exists(createpath, cfg.Fs.Source); x {
|
||||
return newUserError(createpath, "already exists")
|
||||
}
|
||||
|
||||
mkdir(createpath, "layouts", "_default")
|
||||
mkdir(createpath, "layouts", "partials")
|
||||
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "index.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "404.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "list.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "single.html")
|
||||
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "header.html")
|
||||
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "footer.html")
|
||||
|
||||
mkdir(createpath, "archetypes")
|
||||
|
||||
archDefault := []byte("+++\n+++\n")
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), cfg.Fs.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mkdir(createpath, "static", "js")
|
||||
mkdir(createpath, "static", "css")
|
||||
|
||||
by := []byte(`The MIT License (MIT)
|
||||
|
||||
Copyright (c) ` + time.Now().Format("2006") + ` YOUR_NAME_HERE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), cfg.Fs.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.createThemeMD(cfg.Fs, createpath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *newThemeCmd) createThemeMD(fs *hugofs.Fs, inpath string) (err error) {
|
||||
|
||||
by := []byte(`# theme.toml template for a Hugo theme
|
||||
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
|
||||
|
||||
name = "` + strings.Title(helpers.MakeTitle(filepath.Base(inpath))) + `"
|
||||
license = "MIT"
|
||||
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE.md"
|
||||
description = ""
|
||||
homepage = "http://example.com/"
|
||||
tags = []
|
||||
features = []
|
||||
min_version = "0.38"
|
||||
|
||||
[author]
|
||||
name = ""
|
||||
homepage = ""
|
||||
|
||||
# If porting an existing theme
|
||||
[original]
|
||||
name = ""
|
||||
homepage = ""
|
||||
repo = ""
|
||||
`)
|
||||
|
||||
err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), fs.Source)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+11
-4
@@ -18,13 +18,12 @@ package commands
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/releaser"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
HugoCmd.AddCommand(createReleaser().cmd)
|
||||
}
|
||||
var _ cmder = (*releaseCommandeer)(nil)
|
||||
|
||||
type releaseCommandeer struct {
|
||||
cmd *cobra.Command
|
||||
@@ -35,7 +34,7 @@ type releaseCommandeer struct {
|
||||
try bool
|
||||
}
|
||||
|
||||
func createReleaser() *releaseCommandeer {
|
||||
func createReleaser() cmder {
|
||||
// Note: This is a command only meant for internal use and must be run
|
||||
// via "go run -tags release main.go release" on the actual code base that is in the release.
|
||||
r := &releaseCommandeer{
|
||||
@@ -57,6 +56,14 @@ func createReleaser() *releaseCommandeer {
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *releaseCommandeer) getCommand() *cobra.Command {
|
||||
return c.cmd
|
||||
}
|
||||
|
||||
func (c *releaseCommandeer) flagsToConfig(cfg config.Provider) {
|
||||
|
||||
}
|
||||
|
||||
func (r *releaseCommandeer) release() error {
|
||||
if r.version == "" {
|
||||
return errors.New("must set the --rel flag to the relevant version number")
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// +build !release
|
||||
|
||||
// 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 commands
|
||||
|
||||
func createReleaser() cmder {
|
||||
return &nilCommand{}
|
||||
}
|
||||
+149
-100
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 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.
|
||||
@@ -19,10 +19,13 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/livereload"
|
||||
@@ -35,7 +38,10 @@ import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var (
|
||||
type serverCmd struct {
|
||||
// Can be used to stop the server. Useful in tests
|
||||
stop <-chan bool
|
||||
|
||||
disableLiveReload bool
|
||||
navigateToChanged bool
|
||||
renderToDisk bool
|
||||
@@ -47,13 +53,22 @@ var (
|
||||
noHTTPCache bool
|
||||
|
||||
disableFastRender bool
|
||||
)
|
||||
|
||||
var serverCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Aliases: []string{"serve"},
|
||||
Short: "A high performance webserver",
|
||||
Long: `Hugo provides its own webserver which builds and serves the site.
|
||||
*baseBuilderCmd
|
||||
}
|
||||
|
||||
func (b *commandsBuilder) newServerCmd() *serverCmd {
|
||||
return b.newServerCmdSignaled(nil)
|
||||
}
|
||||
|
||||
func (b *commandsBuilder) newServerCmdSignaled(stop <-chan bool) *serverCmd {
|
||||
cc := &serverCmd{stop: stop}
|
||||
|
||||
cc.baseBuilderCmd = b.newBuilderCmd(&cobra.Command{
|
||||
Use: "server",
|
||||
Aliases: []string{"serve"},
|
||||
Short: "A high performance webserver",
|
||||
Long: `Hugo provides its own webserver which builds and serves the site.
|
||||
While hugo server is high performance, it is a webserver with limited options.
|
||||
Many run it in production, but the standard behavior is for people to use it
|
||||
in development and use a more full featured server such as Nginx or Caddy.
|
||||
@@ -65,7 +80,24 @@ By default hugo will also watch your files for any changes you make and
|
||||
automatically rebuild the site. It will then live reload any open browser pages
|
||||
and push the latest content to them. As most Hugo sites are built in a fraction
|
||||
of a second, you will be able to save and see your changes nearly instantly.`,
|
||||
//RunE: server,
|
||||
RunE: cc.server,
|
||||
})
|
||||
|
||||
cc.cmd.Flags().IntVarP(&cc.serverPort, "port", "p", 1313, "port on which the server will listen")
|
||||
cc.cmd.Flags().IntVar(&cc.liveReloadPort, "liveReloadPort", -1, "port for live reloading (i.e. 443 in HTTPS proxy situations)")
|
||||
cc.cmd.Flags().StringVarP(&cc.serverInterface, "bind", "", "127.0.0.1", "interface to which the server will bind")
|
||||
cc.cmd.Flags().BoolVarP(&cc.serverWatch, "watch", "w", true, "watch filesystem for changes and recreate as needed")
|
||||
cc.cmd.Flags().BoolVar(&cc.noHTTPCache, "noHTTPCache", false, "prevent HTTP caching")
|
||||
cc.cmd.Flags().BoolVarP(&cc.serverAppend, "appendPort", "", true, "append port to baseURL")
|
||||
cc.cmd.Flags().BoolVar(&cc.disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild")
|
||||
cc.cmd.Flags().BoolVar(&cc.navigateToChanged, "navigateToChanged", false, "navigate to changed content file on live browser reload")
|
||||
cc.cmd.Flags().BoolVar(&cc.renderToDisk, "renderToDisk", false, "render to Destination path (default is render to memory & serve from there)")
|
||||
cc.cmd.Flags().BoolVar(&cc.disableFastRender, "disableFastRender", false, "enables full re-renders on changes")
|
||||
|
||||
cc.cmd.Flags().String("memstats", "", "log memory usage to this file")
|
||||
cc.cmd.Flags().String("meminterval", "100ms", "interval to poll memory usage (requires --memstats), valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".")
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
type filesOnlyFs struct {
|
||||
@@ -88,106 +120,105 @@ func (f noDirFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
initHugoBuilderFlags(serverCmd)
|
||||
var serverPorts []int
|
||||
|
||||
serverCmd.Flags().IntVarP(&serverPort, "port", "p", 1313, "port on which the server will listen")
|
||||
serverCmd.Flags().IntVar(&liveReloadPort, "liveReloadPort", -1, "port for live reloading (i.e. 443 in HTTPS proxy situations)")
|
||||
serverCmd.Flags().StringVarP(&serverInterface, "bind", "", "127.0.0.1", "interface to which the server will bind")
|
||||
serverCmd.Flags().BoolVarP(&serverWatch, "watch", "w", true, "watch filesystem for changes and recreate as needed")
|
||||
serverCmd.Flags().BoolVar(&noHTTPCache, "noHTTPCache", false, "prevent HTTP caching")
|
||||
serverCmd.Flags().BoolVarP(&serverAppend, "appendPort", "", true, "append port to baseURL")
|
||||
serverCmd.Flags().BoolVar(&disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild")
|
||||
serverCmd.Flags().BoolVar(&navigateToChanged, "navigateToChanged", false, "navigate to changed content file on live browser reload")
|
||||
serverCmd.Flags().BoolVar(&renderToDisk, "renderToDisk", false, "render to Destination path (default is render to memory & serve from there)")
|
||||
serverCmd.Flags().BoolVar(&disableFastRender, "disableFastRender", false, "enables full re-renders on changes")
|
||||
|
||||
serverCmd.Flags().String("memstats", "", "log memory usage to this file")
|
||||
serverCmd.Flags().String("meminterval", "100ms", "interval to poll memory usage (requires --memstats), valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".")
|
||||
|
||||
serverCmd.RunE = server
|
||||
|
||||
}
|
||||
|
||||
func server(cmd *cobra.Command, args []string) error {
|
||||
func (s *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 != "" {
|
||||
renderToDisk = true
|
||||
s.renderToDisk = true
|
||||
}
|
||||
|
||||
var serverCfgInit sync.Once
|
||||
|
||||
cfgInit := func(c *commandeer) error {
|
||||
c.Set("renderToMemory", !renderToDisk)
|
||||
c.Set("renderToMemory", !s.renderToDisk)
|
||||
if cmd.Flags().Changed("navigateToChanged") {
|
||||
c.Set("navigateToChanged", navigateToChanged)
|
||||
c.Set("navigateToChanged", s.navigateToChanged)
|
||||
}
|
||||
if cmd.Flags().Changed("disableLiveReload") {
|
||||
c.Set("disableLiveReload", disableLiveReload)
|
||||
c.Set("disableLiveReload", s.disableLiveReload)
|
||||
}
|
||||
if cmd.Flags().Changed("disableFastRender") {
|
||||
c.Set("disableFastRender", disableFastRender)
|
||||
c.Set("disableFastRender", s.disableFastRender)
|
||||
}
|
||||
if serverWatch {
|
||||
if s.serverWatch {
|
||||
c.Set("watch", true)
|
||||
}
|
||||
|
||||
serverPorts := make([]int, 1)
|
||||
|
||||
if c.languages.IsMultihost() {
|
||||
if !serverAppend {
|
||||
return newSystemError("--appendPort=false not supported when in multihost mode")
|
||||
}
|
||||
serverPorts = make([]int, len(c.languages))
|
||||
// TODO(bep) yes, we should fix.
|
||||
if !c.languagesConfigured {
|
||||
return nil
|
||||
}
|
||||
|
||||
currentServerPort := serverPort
|
||||
var err error
|
||||
|
||||
for i := 0; i < len(serverPorts); i++ {
|
||||
l, err := net.Listen("tcp", net.JoinHostPort(serverInterface, strconv.Itoa(currentServerPort)))
|
||||
if err == nil {
|
||||
l.Close()
|
||||
serverPorts[i] = currentServerPort
|
||||
} else {
|
||||
if i == 0 && serverCmd.Flags().Changed("port") {
|
||||
// port set explicitly by user -- he/she probably meant it!
|
||||
return newSystemErrorF("Server startup failed: %s", err)
|
||||
// We can only do this once.
|
||||
serverCfgInit.Do(func() {
|
||||
serverPorts = make([]int, 1)
|
||||
|
||||
if c.languages.IsMultihost() {
|
||||
if !s.serverAppend {
|
||||
err = newSystemError("--appendPort=false not supported when in multihost mode")
|
||||
}
|
||||
jww.ERROR.Println("port", serverPort, "already in use, attempting to use an available port")
|
||||
sp, err := helpers.FindAvailablePort()
|
||||
if err != nil {
|
||||
return newSystemError("Unable to find alternative port to use:", err)
|
||||
}
|
||||
serverPorts[i] = sp.Port
|
||||
serverPorts = make([]int, len(c.languages))
|
||||
}
|
||||
|
||||
currentServerPort = serverPorts[i] + 1
|
||||
}
|
||||
currentServerPort := s.serverPort
|
||||
|
||||
for i := 0; i < len(serverPorts); i++ {
|
||||
l, err := net.Listen("tcp", net.JoinHostPort(s.serverInterface, strconv.Itoa(currentServerPort)))
|
||||
if err == nil {
|
||||
l.Close()
|
||||
serverPorts[i] = currentServerPort
|
||||
} else {
|
||||
if i == 0 && s.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")
|
||||
sp, err := helpers.FindAvailablePort()
|
||||
if err != nil {
|
||||
err = newSystemError("Unable to find alternative port to use:", err)
|
||||
}
|
||||
serverPorts[i] = sp.Port
|
||||
}
|
||||
|
||||
currentServerPort = serverPorts[i] + 1
|
||||
}
|
||||
})
|
||||
|
||||
c.serverPorts = serverPorts
|
||||
|
||||
c.Set("port", serverPort)
|
||||
if liveReloadPort != -1 {
|
||||
c.Set("liveReloadPort", liveReloadPort)
|
||||
c.Set("port", s.serverPort)
|
||||
if s.liveReloadPort != -1 {
|
||||
c.Set("liveReloadPort", s.liveReloadPort)
|
||||
} else {
|
||||
c.Set("liveReloadPort", serverPorts[0])
|
||||
}
|
||||
|
||||
if c.languages.IsMultihost() {
|
||||
for i, language := range c.languages {
|
||||
baseURL, err := fixURL(language, baseURL, serverPorts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isMultiHost := c.languages.IsMultihost()
|
||||
for i, language := range c.languages {
|
||||
var serverPort int
|
||||
if isMultiHost {
|
||||
serverPort = serverPorts[i]
|
||||
} else {
|
||||
serverPort = serverPorts[0]
|
||||
}
|
||||
|
||||
baseURL, err := s.fixURL(language, s.baseURL, serverPort)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if isMultiHost {
|
||||
language.Set("baseURL", baseURL)
|
||||
}
|
||||
} else {
|
||||
baseURL, err := fixURL(c.Cfg, baseURL, serverPorts[0])
|
||||
if err != nil {
|
||||
return err
|
||||
if i == 0 {
|
||||
c.Set("baseURL", baseURL)
|
||||
}
|
||||
c.Set("baseURL", baseURL)
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
@@ -195,25 +226,21 @@ func server(cmd *cobra.Command, args []string) error {
|
||||
jww.ERROR.Println("memstats error:", err)
|
||||
}
|
||||
|
||||
c, err := InitializeConfig(true, cfgInit, serverCmd)
|
||||
c, err := initializeConfig(true, &s.hugoBuilderCommon, s, cfgInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.build(serverWatch); err != nil {
|
||||
if err := c.serverBuild(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range Hugo.Sites {
|
||||
for _, s := range c.hugo.Sites {
|
||||
s.RegisterMediaTypes()
|
||||
}
|
||||
|
||||
if serverWatch {
|
||||
c.watchConfig()
|
||||
}
|
||||
|
||||
// Watch runs its own server as part of the routine
|
||||
if serverWatch {
|
||||
if s.serverWatch {
|
||||
|
||||
watchDirs, err := c.getDirList()
|
||||
if err != nil {
|
||||
@@ -229,20 +256,25 @@ func server(cmd *cobra.Command, args []string) error {
|
||||
rootWatchDirs := strings.Join(helpers.UniqueStrings(helpers.ExtractRootPaths(relWatchDirs)), ",")
|
||||
|
||||
jww.FEEDBACK.Printf("Watching for changes in %s%s{%s}\n", baseWatchDir, helpers.FilePathSeparator, rootWatchDirs)
|
||||
err = c.newWatcher(true, watchDirs...)
|
||||
watcher, err := c.newWatcher(watchDirs...)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer watcher.Close()
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
return c.serve(s)
|
||||
|
||||
}
|
||||
|
||||
type fileServer struct {
|
||||
baseURLs []string
|
||||
roots []string
|
||||
c *commandeer
|
||||
s *serverCmd
|
||||
}
|
||||
|
||||
func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, error) {
|
||||
@@ -259,7 +291,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
|
||||
absPublishDir := f.c.PathSpec().AbsPathify(publishDir)
|
||||
|
||||
if i == 0 {
|
||||
if renderToDisk {
|
||||
if f.s.renderToDisk {
|
||||
jww.FEEDBACK.Println("Serving pages from " + absPublishDir)
|
||||
} else {
|
||||
jww.FEEDBACK.Println("Serving pages from memory")
|
||||
@@ -269,7 +301,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
|
||||
httpFs := afero.NewHttpFs(f.c.Fs.Destination)
|
||||
fs := filesOnlyFs{httpFs.Dir(absPublishDir)}
|
||||
|
||||
doLiveReload := !buildWatch && !f.c.Cfg.GetBool("disableLiveReload")
|
||||
doLiveReload := !f.s.buildWatch && !f.c.Cfg.GetBool("disableLiveReload")
|
||||
fastRenderMode := doLiveReload && !f.c.Cfg.GetBool("disableFastRender")
|
||||
|
||||
if i == 0 && fastRenderMode {
|
||||
@@ -284,7 +316,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
|
||||
|
||||
decorate := func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if noHTTPCache {
|
||||
if f.s.noHTTPCache {
|
||||
w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
}
|
||||
@@ -308,14 +340,14 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
|
||||
mu.Handle(u.Path, http.StripPrefix(u.Path, fileserver))
|
||||
}
|
||||
|
||||
endpoint := net.JoinHostPort(serverInterface, strconv.Itoa(port))
|
||||
endpoint := net.JoinHostPort(f.s.serverInterface, strconv.Itoa(port))
|
||||
|
||||
return mu, u.String(), endpoint, nil
|
||||
}
|
||||
|
||||
func (c *commandeer) serve() {
|
||||
func (c *commandeer) serve(s *serverCmd) error {
|
||||
|
||||
isMultiHost := Hugo.IsMultihost()
|
||||
isMultiHost := c.hugo.IsMultihost()
|
||||
|
||||
var (
|
||||
baseURLs []string
|
||||
@@ -323,12 +355,12 @@ func (c *commandeer) serve() {
|
||||
)
|
||||
|
||||
if isMultiHost {
|
||||
for _, s := range Hugo.Sites {
|
||||
for _, s := range c.hugo.Sites {
|
||||
baseURLs = append(baseURLs, s.BaseURL.String())
|
||||
roots = append(roots, s.Language.Lang)
|
||||
}
|
||||
} else {
|
||||
s := Hugo.Sites[0]
|
||||
s := c.hugo.Sites[0]
|
||||
baseURLs = []string{s.BaseURL.String()}
|
||||
roots = []string{""}
|
||||
}
|
||||
@@ -337,6 +369,7 @@ func (c *commandeer) serve() {
|
||||
baseURLs: baseURLs,
|
||||
roots: roots,
|
||||
c: c,
|
||||
s: s,
|
||||
}
|
||||
|
||||
doLiveReload := !c.Cfg.GetBool("disableLiveReload")
|
||||
@@ -345,14 +378,17 @@ func (c *commandeer) serve() {
|
||||
livereload.Initialize()
|
||||
}
|
||||
|
||||
for i, _ := range baseURLs {
|
||||
var sigs = make(chan os.Signal)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
for i := range baseURLs {
|
||||
mu, serverURL, endpoint, err := srv.createEndpoint(i)
|
||||
|
||||
if doLiveReload {
|
||||
mu.HandleFunc("/livereload.js", livereload.ServeJS)
|
||||
mu.HandleFunc("/livereload", livereload.Handler)
|
||||
}
|
||||
jww.FEEDBACK.Printf("Web Server is available at %s (bind address %s)\n", serverURL, serverInterface)
|
||||
jww.FEEDBACK.Printf("Web Server is available at %s (bind address %s)\n", serverURL, s.serverInterface)
|
||||
go func() {
|
||||
err = http.ListenAndServe(endpoint, mu)
|
||||
if err != nil {
|
||||
@@ -363,11 +399,22 @@ func (c *commandeer) serve() {
|
||||
}
|
||||
|
||||
jww.FEEDBACK.Println("Press Ctrl+C to stop")
|
||||
|
||||
if s.stop != nil {
|
||||
select {
|
||||
case <-sigs:
|
||||
case <-s.stop:
|
||||
}
|
||||
} else {
|
||||
<-sigs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixURL massages the baseURL into a form needed for serving
|
||||
// all pages correctly.
|
||||
func fixURL(cfg config.Provider, s string, port int) (string, error) {
|
||||
func (sc *serverCmd) fixURL(cfg config.Provider, s string, port int) (string, error) {
|
||||
useLocalhost := false
|
||||
if s == "" {
|
||||
s = cfg.GetString("baseURL")
|
||||
@@ -402,7 +449,7 @@ func fixURL(cfg config.Provider, s string, port int) (string, error) {
|
||||
u.Host = "localhost"
|
||||
}
|
||||
|
||||
if serverAppend {
|
||||
if sc.serverAppend {
|
||||
if strings.Contains(u.Host, ":") {
|
||||
u.Host, _, err = net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
@@ -416,9 +463,11 @@ func fixURL(cfg config.Provider, s string, port int) (string, error) {
|
||||
}
|
||||
|
||||
func memStats() error {
|
||||
memstats := serverCmd.Flags().Lookup("memstats").Value.String()
|
||||
b := newCommandsBuilder()
|
||||
sc := b.newServerCmd().getCommand()
|
||||
memstats := sc.Flags().Lookup("memstats").Value.String()
|
||||
if memstats != "" {
|
||||
interval, err := time.ParseDuration(serverCmd.Flags().Lookup("meminterval").Value.String())
|
||||
interval, err := time.ParseDuration(sc.Flags().Lookup("meminterval").Value.String())
|
||||
if err != nil {
|
||||
interval, _ = time.ParseDuration("100ms")
|
||||
}
|
||||
|
||||
+55
-4
@@ -14,11 +14,60 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
dir, err := createSimpleTestSite(t)
|
||||
assert.NoError(err)
|
||||
|
||||
// Let us hope that this port is available on all systems ...
|
||||
port := 1331
|
||||
|
||||
defer func() {
|
||||
os.RemoveAll(dir)
|
||||
}()
|
||||
|
||||
stop := make(chan bool)
|
||||
|
||||
b := newCommandsBuilder()
|
||||
scmd := b.newServerCmdSignaled(stop)
|
||||
|
||||
cmd := scmd.getCommand()
|
||||
cmd.SetArgs([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)})
|
||||
|
||||
go func() {
|
||||
_, err = cmd.ExecuteC()
|
||||
assert.NoError(err)
|
||||
}()
|
||||
|
||||
// There is no way to know exactly when the server is ready for connections.
|
||||
// We could improve by something like https://golang.org/pkg/net/http/httptest/#Server
|
||||
// But for now, let us sleep and pray!
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
resp, err := http.Get("http://localhost:1331/")
|
||||
assert.NoError(err)
|
||||
defer resp.Body.Close()
|
||||
homeContent := helpers.ReaderToString(resp.Body)
|
||||
|
||||
assert.Contains(homeContent, "List: Hugo Commands")
|
||||
|
||||
// Stop the server.
|
||||
stop <- true
|
||||
|
||||
}
|
||||
|
||||
func TestFixURL(t *testing.T) {
|
||||
type data struct {
|
||||
TestName string
|
||||
@@ -42,12 +91,14 @@ func TestFixURL(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
b := newCommandsBuilder()
|
||||
s := b.newServerCmd()
|
||||
v := viper.New()
|
||||
baseURL = test.CLIBaseURL
|
||||
baseURL := test.CLIBaseURL
|
||||
v.Set("baseURL", test.CfgBaseURL)
|
||||
serverAppend = test.AppendPort
|
||||
serverPort = test.Port
|
||||
result, err := fixURL(v, baseURL, serverPort)
|
||||
s.serverAppend = test.AppendPort
|
||||
s.serverPort = test.Port
|
||||
result, err := s.fixURL(v, baseURL, s.serverPort)
|
||||
if err != nil {
|
||||
t.Errorf("Test #%d %s: unexpected error %s", i, test.TestName, err)
|
||||
}
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
// Copyright 2015 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var undraftCmd = &cobra.Command{
|
||||
Use: "undraft path/to/content",
|
||||
Short: "Undraft resets the content's draft status",
|
||||
Long: `Undraft resets the content's draft status
|
||||
and updates the date to the current date and time.
|
||||
If the content's draft status is 'False', nothing is done.`,
|
||||
RunE: Undraft,
|
||||
}
|
||||
|
||||
// Undraft publishes the specified content by setting its draft status
|
||||
// to false and setting its publish date to now. If the specified content is
|
||||
// not a draft, it will log an error.
|
||||
func Undraft(cmd *cobra.Command, args []string) error {
|
||||
c, err := InitializeConfig(false, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
return newUserError("a piece of content needs to be specified")
|
||||
}
|
||||
|
||||
cfg := c.DepsCfg
|
||||
|
||||
location := args[0]
|
||||
// open the file
|
||||
f, err := cfg.Fs.Source.Open(location)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the page from file
|
||||
p, err := parser.ReadFrom(f)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := undraftContent(p)
|
||||
if err != nil {
|
||||
return newSystemErrorF("an error occurred while undrafting %q: %s", location, err)
|
||||
}
|
||||
|
||||
f, err = cfg.Fs.Source.OpenFile(location, os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return newSystemErrorF("%q not be undrafted due to error opening file to save changes: %q\n", location, err)
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = w.WriteTo(f)
|
||||
if err != nil {
|
||||
return newSystemErrorF("%q not be undrafted due to save error: %q\n", location, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// undraftContent: if the content is a draft, change its draft status to
|
||||
// 'false' and set the date to time.Now(). If the draft status is already
|
||||
// 'false', don't do anything.
|
||||
func undraftContent(p parser.Page) (bytes.Buffer, error) {
|
||||
var buff bytes.Buffer
|
||||
// get the metadata; easiest way to see if it's a draft
|
||||
meta, err := p.Metadata()
|
||||
if err != nil {
|
||||
return buff, err
|
||||
}
|
||||
// since the metadata was obtainable, we can also get the key/value separator for
|
||||
// Front Matter
|
||||
fm := p.FrontMatter()
|
||||
if fm == nil {
|
||||
return buff, errors.New("Front Matter was found, nothing was finalized")
|
||||
}
|
||||
|
||||
var isDraft, gotDate bool
|
||||
var date string
|
||||
L:
|
||||
for k, v := range meta.(map[string]interface{}) {
|
||||
switch k {
|
||||
case "draft":
|
||||
if !v.(bool) {
|
||||
return buff, errors.New("not a Draft: nothing was done")
|
||||
}
|
||||
isDraft = true
|
||||
if gotDate {
|
||||
break L
|
||||
}
|
||||
case "date":
|
||||
date = v.(string) // capture the value to make replacement easier
|
||||
gotDate = true
|
||||
if isDraft {
|
||||
break L
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if draft wasn't found in FrontMatter, it isn't a draft.
|
||||
if !isDraft {
|
||||
return buff, errors.New("not a Draft: nothing was done")
|
||||
}
|
||||
|
||||
// get the front matter as bytes and split it into lines
|
||||
var lineEnding []byte
|
||||
fmLines := bytes.Split(fm, []byte("\n"))
|
||||
if len(fmLines) == 1 { // if the result is only 1 element, try to split on dos line endings
|
||||
fmLines = bytes.Split(fm, []byte("\r\n"))
|
||||
if len(fmLines) == 1 {
|
||||
return buff, errors.New("unable to split FrontMatter into lines")
|
||||
}
|
||||
lineEnding = append(lineEnding, []byte("\r\n")...)
|
||||
} else {
|
||||
lineEnding = append(lineEnding, []byte("\n")...)
|
||||
}
|
||||
|
||||
// Write the front matter lines to the buffer, replacing as necessary
|
||||
for _, v := range fmLines {
|
||||
pos := bytes.Index(v, []byte("draft"))
|
||||
if pos != -1 {
|
||||
continue
|
||||
}
|
||||
pos = bytes.Index(v, []byte("date"))
|
||||
if pos != -1 { // if date field wasn't found, add it
|
||||
v = bytes.Replace(v, []byte(date), []byte(time.Now().Format(time.RFC3339)), 1)
|
||||
}
|
||||
buff.Write(v)
|
||||
buff.Write(lineEnding)
|
||||
}
|
||||
|
||||
// append the actual content
|
||||
buff.Write(p.Content())
|
||||
|
||||
return buff, nil
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright 2015 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package commands
|
||||
|
||||
// TODO Support Mac Encoding (\r)
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
)
|
||||
|
||||
var (
|
||||
jsonFM = "{\n \"date\": \"12-04-06\",\n \"title\": \"test json\"\n}"
|
||||
jsonDraftFM = "{\n \"draft\": true,\n \"date\": \"12-04-06\",\n \"title\":\"test json\"\n}"
|
||||
tomlFM = "+++\n date= \"12-04-06\"\n title= \"test toml\"\n+++"
|
||||
tomlDraftFM = "+++\n draft= true\n date= \"12-04-06\"\n title=\"test toml\"\n+++"
|
||||
yamlFM = "---\n date: \"12-04-06\"\n title: \"test yaml\"\n---"
|
||||
yamlDraftFM = "---\n draft: true\n date: \"12-04-06\"\n title: \"test yaml\"\n---"
|
||||
yamlYesDraftFM = "---\n draft: yes\n date: \"12-04-06\"\n title: \"test yaml\"\n---"
|
||||
)
|
||||
|
||||
func TestUndraftContent(t *testing.T) {
|
||||
tests := []struct {
|
||||
fm string
|
||||
expectedErr string
|
||||
}{
|
||||
{jsonFM, "not a Draft: nothing was done"},
|
||||
{jsonDraftFM, ""},
|
||||
{tomlFM, "not a Draft: nothing was done"},
|
||||
{tomlDraftFM, ""},
|
||||
{yamlFM, "not a Draft: nothing was done"},
|
||||
{yamlDraftFM, ""},
|
||||
{yamlYesDraftFM, ""},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
r := bytes.NewReader([]byte(test.fm))
|
||||
p, _ := parser.ReadFrom(r)
|
||||
res, err := undraftContent(p)
|
||||
if test.expectedErr != "" {
|
||||
if err == nil {
|
||||
t.Errorf("[%d] Expected error, got none", i)
|
||||
continue
|
||||
}
|
||||
if err.Error() != test.expectedErr {
|
||||
t.Errorf("[%d] Expected %q, got %q", i, test.expectedErr, err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
r = bytes.NewReader(res.Bytes())
|
||||
p, _ = parser.ReadFrom(r)
|
||||
meta, err := p.Metadata()
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error %q", i, err)
|
||||
continue
|
||||
}
|
||||
for k, v := range meta.(map[string]interface{}) {
|
||||
if k == "draft" {
|
||||
if v.(bool) {
|
||||
t.Errorf("[%d] Expected %q to be \"false\", got \"true\"", i, k)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if k == "date" {
|
||||
if !strings.HasPrefix(v.(string), time.Now().Format("2006-01-02")) {
|
||||
t.Errorf("[%d] Expected %v to start with %v", i, v.(string), time.Now().Format("2006-01-02"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-46
@@ -14,67 +14,39 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/kardianos/osext"
|
||||
"github.com/spf13/cobra"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version number of Hugo",
|
||||
Long: `All software has versions. This is Hugo's.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
printHugoVersion()
|
||||
return nil
|
||||
},
|
||||
var _ cmder = (*versionCmd)(nil)
|
||||
|
||||
type versionCmd struct {
|
||||
*baseCmd
|
||||
}
|
||||
|
||||
func newVersionCmd() *versionCmd {
|
||||
return &versionCmd{
|
||||
newBaseCmd(&cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version number of Hugo",
|
||||
Long: `All software has versions. This is Hugo's.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
printHugoVersion()
|
||||
return nil
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func printHugoVersion() {
|
||||
if hugolib.BuildDate == "" {
|
||||
setBuildDate() // set the build date from executable's mdate
|
||||
} else {
|
||||
formatBuildDate() // format the compile time
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// setBuildDate checks the ModTime of the Hugo executable and returns it as a
|
||||
// formatted string. This assumes that the executable name is Hugo, if it does
|
||||
// not exist, an empty string will be returned. This is only called if the
|
||||
// hugolib.BuildDate wasn't set during compile time.
|
||||
//
|
||||
// osext is used for cross-platform.
|
||||
func setBuildDate() {
|
||||
fname, _ := osext.Executable()
|
||||
dir, err := filepath.Abs(filepath.Dir(fname))
|
||||
if err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
return
|
||||
}
|
||||
fi, err := os.Lstat(filepath.Join(dir, filepath.Base(fname)))
|
||||
if err != nil {
|
||||
jww.ERROR.Println(err)
|
||||
return
|
||||
}
|
||||
t := fi.ModTime()
|
||||
hugolib.BuildDate = t.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// formatBuildDate formats the hugolib.BuildDate according to the value in
|
||||
// .Params.DateFormat, if it's set.
|
||||
func formatBuildDate() {
|
||||
t, _ := time.Parse("2006-01-02T15:04:05-0700", hugolib.BuildDate)
|
||||
hugolib.BuildDate = t.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ import (
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// KeyValueStr is a string tuple.
|
||||
type KeyValueStr struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// KeyValues holds an key and a slice of values.
|
||||
type KeyValues struct {
|
||||
Key interface{}
|
||||
|
||||
@@ -19,3 +19,11 @@ package compare
|
||||
type Eqer interface {
|
||||
Eq(other interface{}) bool
|
||||
}
|
||||
|
||||
// Comparer can be used to compare two values.
|
||||
// This will be used when using the le, ge etc. operators in the templates.
|
||||
// Compare returns -1 if the given version is less than, 0 if equal and 1 if greater than
|
||||
// the running version.
|
||||
type Comparer interface {
|
||||
Compare(other interface{}) int
|
||||
}
|
||||
@@ -20,6 +20,7 @@ type Provider interface {
|
||||
GetBool(key string) bool
|
||||
GetStringMap(key string) map[string]interface{}
|
||||
GetStringMapString(key string) map[string]string
|
||||
GetStringSlice(key string) []string
|
||||
Get(key string) interface{}
|
||||
Set(key string, value interface{})
|
||||
IsSet(key string) bool
|
||||
|
||||
+16
-1
@@ -63,7 +63,22 @@ func NewContent(
|
||||
return err
|
||||
}
|
||||
|
||||
contentPath := s.PathSpec.AbsPathify(filepath.Join(s.Cfg.GetString("contentDir"), targetPath))
|
||||
// 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
|
||||
|
||||
@@ -16,6 +16,7 @@ package create
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -43,6 +44,12 @@ type ArchetypeFileData struct {
|
||||
// on the presence of language code in the filename.
|
||||
Site *hugolib.Site
|
||||
|
||||
// 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.
|
||||
// This is the value you in most cases would want to use to construct the title in your
|
||||
// archetype template.
|
||||
Name string
|
||||
|
||||
// The target content file. Note that the .Content will be empty, as that
|
||||
// has not been created yet.
|
||||
source.File
|
||||
@@ -51,7 +58,7 @@ type ArchetypeFileData struct {
|
||||
const (
|
||||
// ArchetypeTemplateTemplate is used as initial template when adding an archetype template.
|
||||
ArchetypeTemplateTemplate = `---
|
||||
title: "{{ replace .TranslationBaseName "-" " " | title }}"
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
@@ -81,12 +88,25 @@ func executeArcheTypeAsTemplate(s *hugolib.Site, kind, targetPath, archetypeFile
|
||||
err error
|
||||
)
|
||||
|
||||
sp := source.NewSourceSpec(s.Deps.Cfg, s.Deps.Fs)
|
||||
f := sp.NewFileInfo("", targetPath, nil)
|
||||
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)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
data := ArchetypeFileData{
|
||||
Type: kind,
|
||||
Date: time.Now().Format(time.RFC3339),
|
||||
Name: name,
|
||||
File: f,
|
||||
Site: s,
|
||||
}
|
||||
@@ -122,15 +142,6 @@ func executeArcheTypeAsTemplate(s *hugolib.Site, kind, targetPath, archetypeFile
|
||||
|
||||
archetypeContent = []byte(archetypeShortcodeReplacementsPost.Replace(buff.String()))
|
||||
|
||||
if !bytes.Contains(archetypeContent, []byte("date")) || !bytes.Contains(archetypeContent, []byte("title")) {
|
||||
// TODO(bep) remove some time in the future.
|
||||
s.Log.FEEDBACK.Println(fmt.Sprintf(`WARNING: date and/or title missing from archetype file %q.
|
||||
From Hugo 0.24 this must be provided in the archetype file itself, if needed. Example:
|
||||
%s
|
||||
`, archetypeFilename, ArchetypeTemplateTemplate))
|
||||
|
||||
}
|
||||
|
||||
return archetypeContent, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func TestNewContent(t *testing.T) {
|
||||
for i, v := range c.expected {
|
||||
found := strings.Contains(content, v)
|
||||
if !found {
|
||||
t.Errorf("[%d] %q missing from output:\n%q", i, v, content)
|
||||
t.Fatalf("[%d] %q missing from output:\n%q", i, v, content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -126,7 +126,7 @@ func New(cfg DepsCfg) (*Deps, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sp := source.NewSourceSpec(cfg.Language, fs)
|
||||
sp := source.NewSourceSpec(ps, fs.Source)
|
||||
|
||||
d := &Deps{
|
||||
Fs: fs,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/.idea
|
||||
/public
|
||||
nohup.out
|
||||
.DS_Store
|
||||
|
||||
@@ -10,6 +10,12 @@ Note that this repository contains solely the documentation for Hugo. For contri
|
||||
|
||||
*Pull requests shall **only** contain changes to the actual documentation. However, changes on the code base of Hugo **and** the documentation shall be a single, atomic pull request in the [hugo](https://github.com/gohugoio/hugo) repository.*
|
||||
|
||||
Spelling fixes are most welcomed, and if you want to contribute longer sections to the documentation, it would be great if you had these in mind when writing:
|
||||
|
||||
* Short is good. People go to the library to read novels. If there is more than one way to _do a thing_ in Hugo, describe the current _best practice_ (avoid "… but you can also do …" and "… in older versions of Hugo you had to …".
|
||||
* For examples, try to find short snippets that teaches people about the concept. If the example is also useful as-is (copy and paste), then great, but don't list long and similar examples just so people can use them on their sites.
|
||||
* Hugo has users from all over the world, so an easy to understand and [simple English](https://simple.wikipedia.org/wiki/Basic_English) is good.
|
||||
|
||||
## Branches
|
||||
|
||||
* The `master` branch is where the site is automatically built from, and is the place to put changes relevant to the current Hugo version.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
---
|
||||
title: "{{ replace .TranslationBaseName "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
linktitle: ""
|
||||
description: ""
|
||||
godocref: ""
|
||||
publishdate: ""
|
||||
lastmod: ""
|
||||
categories: []
|
||||
keywords: []
|
||||
tags: []
|
||||
weight: 00
|
||||
slug: ""
|
||||
aliases: []
|
||||
toc: false
|
||||
draft: true
|
||||
---
|
||||
---
|
||||
+22
-4
@@ -16,7 +16,6 @@ pluralizeListTitles = false
|
||||
# We do redirects via Netlify's _redirects file, generated by Hugo (see "outputs" below).
|
||||
disableAliases = true
|
||||
|
||||
|
||||
# Highlighting config (Pygments)
|
||||
# It is (currently) not in use, but you can do ```go in a content file if you want to.
|
||||
pygmentsCodeFences = true
|
||||
@@ -27,7 +26,7 @@ pygmentsUseClasses = true
|
||||
pygmentsUseClassic = false
|
||||
|
||||
# See https://help.farbox.com/pygments.html
|
||||
pygmentsStyle = "friendly"
|
||||
pygmentsStyle = "trac"
|
||||
|
||||
[outputs]
|
||||
home = [ "HTML", "RSS", "REDIR", "HEADERS" ]
|
||||
@@ -71,7 +70,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.32"
|
||||
release = "0.39"
|
||||
## Setting this to true will add a "noindex" to *EVERY* page on the site
|
||||
removefromexternalsearch = false
|
||||
## Gh repo for site footer (include trailing slash)
|
||||
@@ -98,10 +97,23 @@ twitter = "GoHugoIO"
|
||||
## Configuration for BlackFriday markdown parser: https://github.com/russross/blackfriday
|
||||
[blackfriday]
|
||||
plainIDAnchors = true
|
||||
hrefTargetBlank = true
|
||||
# See https://github.com/gohugoio/hugo/issues/2424
|
||||
hrefTargetBlank = false
|
||||
angledQuotes = false
|
||||
latexDashes = true
|
||||
|
||||
[imaging]
|
||||
# See https://github.com/disintegration/imaging
|
||||
# CatmullRom is a sharp bicubic filter which should fit the docs site well with its many screenshots.
|
||||
# Note that you can also set this per image processing.
|
||||
resampleFilter = "CatmullRom"
|
||||
|
||||
# Defatult JPEG quality setting. Default is 75.
|
||||
quality = 75
|
||||
|
||||
anchor = "smart"
|
||||
|
||||
|
||||
## As of v0.20, all content files include a default "categories" value that's the same as the section. This was a cheap future-proofing method and should/could be changed accordingly.
|
||||
[taxonomies]
|
||||
category = "categories"
|
||||
@@ -237,6 +249,12 @@ twitter = "GoHugoIO"
|
||||
identifier = "themes"
|
||||
url = "https://themes.gohugo.io/"
|
||||
|
||||
[[menu.global]]
|
||||
name = "Showcase"
|
||||
weight = 20
|
||||
identifier = "showcase"
|
||||
url = "/showcase/"
|
||||
|
||||
# Anything with a weight > 100 gets an external icon
|
||||
[[menu.global]]
|
||||
name = "Community"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "A Fast and Flexible Website Generator"
|
||||
title: "The world’s fastest framework for building websites"
|
||||
date: 2017-03-02T12:00:00-05:00
|
||||
features:
|
||||
- heading: Blistering Speed
|
||||
|
||||
@@ -29,14 +29,14 @@ This has many benefits. The most noticeable is performance. HTTP servers are *ve
|
||||
|
||||
* ["An Introduction to Static Site Generators", David Walsh][]
|
||||
* ["Hugo vs. Wordpress page load speed comparison: Hugo leaves WordPress in its dust", GettingThingsTech][hugovwordpress]
|
||||
* ["Static Site Generators", O-Reilly][]
|
||||
* ["Static Site Generators", O'Reilly][]
|
||||
* [StaticGen: Top Open-Source Static Site Generators (GitHub Stars)][]
|
||||
* ["Top 10 Static Website Generators", Netlify blog][]
|
||||
* ["The Resurgence of Static", dotCMS][dotcms]
|
||||
|
||||
|
||||
["An Introduction to Static Site Generators", David Walsh]: https://davidwalsh.name/introduction-static-site-generators
|
||||
["Static Site Generators", O-Reilly]: /documents/oreilly-static-site-generators.pdf
|
||||
["Static Site Generators", O'Reilly]: http://www.oreilly.com/web-platform/free/files/static-site-generators.pdf
|
||||
["Top 10 Static Website Generators", Netlify blog]: https://www.netlify.com/blog/2016/05/02/top-ten-static-website-generators/
|
||||
[hugovwordpress]: https://gettingthingstech.com/hugo-vs.-wordpress-page-load-speed-comparison-hugo-leaves-wordpress-in-its-dust/
|
||||
[StaticGen: Top Open-Source Static Site Generators (GitHub Stars)]: https://www.staticgen.com/
|
||||
|
||||
@@ -55,7 +55,6 @@ toc: true
|
||||
* Support for [Go][], [Amber], and [Ace][] HTML templates
|
||||
* [Syntax highlighting][] powered by [Pygments][]
|
||||
|
||||
See what's coming next in the [Hugo roadmap][].
|
||||
|
||||
[Ace]: /templates/alternatives/
|
||||
[aliases]: /content-management/urls/#aliases
|
||||
@@ -71,7 +70,6 @@ See what's coming next in the [Hugo roadmap][].
|
||||
[Google Analytics]: https://google-analytics.com/
|
||||
[homepage]: /templates/homepage/
|
||||
[hostanywhere]: /hosting-and-deployment/
|
||||
[Hugo roadmap]: /about/roadmap
|
||||
[install]: /getting-started/installing/
|
||||
[LiveReload]: /getting-started/usage/
|
||||
[organization for your projects]: /getting-started/directory-structure/
|
||||
|
||||
@@ -12,6 +12,8 @@ sections_weight: 10
|
||||
draft: false
|
||||
aliases: []
|
||||
toc: true
|
||||
images:
|
||||
- images/blog/sunset.jpg
|
||||
---
|
||||
|
||||
|
||||
@@ -59,7 +61,7 @@ Note that changes to any resource inside the `content` folder will trigger a rel
|
||||
|
||||
#### List all Resources
|
||||
|
||||
```html
|
||||
```go-html-template
|
||||
{{ range .Resources }}
|
||||
<li><a href="{{ .RelPermalink }}">{{ .ResourceType | title }}</a></li>
|
||||
{{ end }}
|
||||
@@ -71,7 +73,7 @@ For an absolute URL, use `.Permalink`.
|
||||
|
||||
#### List All Resources by Type
|
||||
|
||||
```html
|
||||
```go-html-template
|
||||
{{ with .Resources.ByType "image" }}
|
||||
{{ end }}
|
||||
|
||||
@@ -81,7 +83,7 @@ Type here is `page` for pages, else the main type in the MIME type, so `image`,
|
||||
|
||||
#### Get a Specific Resource
|
||||
|
||||
```html
|
||||
```go-html-template
|
||||
{{ $logo := .Resources.GetByPrefix "logo" }}
|
||||
{{ with $logo }}
|
||||
{{ end }}
|
||||
@@ -89,7 +91,7 @@ Type here is `page` for pages, else the main type in the MIME type, so `image`,
|
||||
|
||||
#### Include Page Resource Content
|
||||
|
||||
```html
|
||||
```go-html-template
|
||||
{{ with .Resources.ByType "page" }}
|
||||
{{ range . }}
|
||||
<h3>{{ .Title }}</h3>
|
||||
@@ -119,6 +121,35 @@ Image operations in Hugo currently **do not preserve EXIF data** as this is not
|
||||
{{% /note %}}
|
||||
|
||||
|
||||
### Image Processing Examples
|
||||
|
||||
_The photo of the sunset used in the examples below is Copyright [Bjørn Erik Pedersen](https://commons.wikimedia.org/wiki/User:Bep) (Creative Commons Attribution-Share Alike 4.0 International license)_
|
||||
|
||||
|
||||
{{< imgproc sunset Resize "300x" />}}
|
||||
|
||||
{{< imgproc sunset Fill "90x120 left" />}}
|
||||
|
||||
{{< imgproc sunset Fill "90x120 right" />}}
|
||||
|
||||
{{< imgproc sunset Fit "90x90" />}}
|
||||
|
||||
{{< imgproc sunset Resize "300x q10" />}}
|
||||
|
||||
|
||||
This is the shortcode used in the examples above:
|
||||
|
||||
|
||||
{{< code file="layouts/shortcodes/imgproc.html" >}}
|
||||
{{< readfile file="layouts/shortcodes/imgproc.html" >}}
|
||||
{{< /code >}}
|
||||
|
||||
And it is used like this:
|
||||
|
||||
```go-html-template
|
||||
{{</* imgproc sunset Resize "300x" */>}}
|
||||
```
|
||||
|
||||
### Image Processing Options
|
||||
|
||||
In addition to the dimensions (e.g. `200x100`) where either height or width can be omitted, Hugo supports a set of additional image options:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
@@ -7,7 +7,7 @@ publishdate: 2017-02-01
|
||||
lastmod: 2017-02-01
|
||||
layout: single
|
||||
menu:
|
||||
main:
|
||||
docs:
|
||||
parent: "about"
|
||||
weight: 10
|
||||
weight: 10
|
||||
@@ -19,7 +19,7 @@ toc: true
|
||||
|
||||
Hugo is a general-purpose website framework. Technically speaking, Hugo is a [static site generator][]. Unlike systems that dynamically build a page with each visitor request, Hugo builds pages when you create or update your content. Since websites are viewed far more often than they are edited, Hugo is designed to provide an optimal viewing experience for your website's end users and an ideal writing experience for website authors.
|
||||
|
||||
Websites built with Hugo are extremely fast and secure. Hugo sites can be hosted anywhere, including [Netlify][], [Heroku][], [GoDaddy][], [DreamHost][], [GitHub Pages][], [Surge][], [Aerobatic][], [Firebase][], [Google Cloud Storage][], [Amazon S3][], [Rackspace][], [Azure][], and [CloudFront][] and work well with CDNs. Hugo sites run without the need for a database or dependencies on expensive runtimes like Ruby, Python, or PHP.
|
||||
Websites built with Hugo are extremely fast and secure. Hugo sites can be hosted anywhere, including [Netlify][], [Heroku][], [GoDaddy][], [DreamHost][], [GitHub Pages][], [GitLab Pages][], [Surge][], [Aerobatic][], [Firebase][], [Google Cloud Storage][], [Amazon S3][], [Rackspace][], [Azure][], and [CloudFront][] and work well with CDNs. Hugo sites run without the need for a database or dependencies on expensive runtimes like Ruby, Python, or PHP.
|
||||
|
||||
We think of Hugo as the ideal website creation tool with nearly instant build times, able to rebuild whenever a change is made.
|
||||
|
||||
@@ -50,7 +50,7 @@ Hugo is for people building a blog, a company site, a portfolio site, documentat
|
||||
[DreamHost]: http://www.dreamhost.com/
|
||||
[Firebase]: https://firebase.google.com/docs/hosting/ "Firebase static hosting"
|
||||
[GitHub Pages]: https://pages.github.com/
|
||||
[GitLab]: https://about.gitlab.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/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo"
|
||||
slug: hugo
|
||||
url: /commands/hugo/
|
||||
@@ -10,7 +10,6 @@ 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
|
||||
@@ -30,16 +29,13 @@ hugo [flags]
|
||||
-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 if true, all relative URLs will be canonicalized using baseURL
|
||||
--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
|
||||
--disable404 do not render 404 page
|
||||
--disableKinds stringSlice disable different kind of pages (home, RSS etc.)
|
||||
--disableRSS do not build RSS files
|
||||
--disableSitemap do not build Sitemap file
|
||||
--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
|
||||
@@ -51,8 +47,8 @@ hugo [flags]
|
||||
--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 pluralize titles in lists using inflect (default true)
|
||||
--preserveTaxonomyNames preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")
|
||||
--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
|
||||
@@ -61,13 +57,14 @@ hugo [flags]
|
||||
--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 if true, use /filename.html instead of /filename/
|
||||
--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
|
||||
@@ -78,7 +75,6 @@ hugo [flags]
|
||||
* [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 undraft](/commands/hugo_undraft/) - Undraft resets the content's draft status
|
||||
* [hugo version](/commands/hugo_version/) - Print the version number of Hugo
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo benchmark"
|
||||
slug: hugo_benchmark
|
||||
url: /commands/hugo_benchmark/
|
||||
@@ -10,7 +10,6 @@ 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.
|
||||
|
||||
@@ -26,16 +25,13 @@ hugo benchmark [flags]
|
||||
-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 if true, all relative URLs will be canonicalized using baseURL
|
||||
--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
|
||||
--disable404 do not render 404 page
|
||||
--disableKinds stringSlice disable different kind of pages (home, RSS etc.)
|
||||
--disableRSS do not build RSS files
|
||||
--disableSitemap do not build Sitemap file
|
||||
--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
|
||||
@@ -46,8 +42,8 @@ hugo benchmark [flags]
|
||||
--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 pluralize titles in lists using inflect (default true)
|
||||
--preserveTaxonomyNames preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")
|
||||
--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
|
||||
@@ -55,7 +51,7 @@ hugo benchmark [flags]
|
||||
--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 if true, use /filename.html instead of /filename/
|
||||
--uglyURLs (deprecated) if true, use /filename.html instead of /filename/
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -71,6 +67,7 @@ hugo benchmark [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo check"
|
||||
slug: hugo_check
|
||||
url: /commands/hugo_check/
|
||||
@@ -10,7 +10,6 @@ Contains some verification checks
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Contains some verification checks
|
||||
|
||||
### Options
|
||||
@@ -32,7 +31,8 @@ Contains some verification checks
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
* [hugo check ulimit](/commands/hugo_check_ulimit/) - Check system ulimit settings
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo check ulimit"
|
||||
slug: hugo_check_ulimit
|
||||
url: /commands/hugo_check_ulimit/
|
||||
@@ -10,7 +10,6 @@ Check system ulimit settings
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Hugo will inspect the current ulimit settings on the system.
|
||||
This is primarily to ensure that Hugo can watch enough files on some OSs
|
||||
|
||||
@@ -37,6 +36,7 @@ hugo check ulimit [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo check](/commands/hugo_check/) - Contains some verification checks
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo config"
|
||||
slug: hugo_config
|
||||
url: /commands/hugo_config/
|
||||
@@ -10,7 +10,6 @@ Print the site configuration
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Print the site configuration, both default and custom settings.
|
||||
|
||||
```
|
||||
@@ -36,6 +35,7 @@ hugo config [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo convert"
|
||||
slug: hugo_convert
|
||||
url: /commands/hugo_convert/
|
||||
@@ -10,7 +10,6 @@ Convert your content to different formats
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Convert your content (e.g. front matter) to different formats.
|
||||
|
||||
See convert's subcommands toJSON, toTOML and toYAML for more information.
|
||||
@@ -37,9 +36,10 @@ See convert's subcommands toJSON, toTOML and toYAML for more information.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
* [hugo convert toJSON](/commands/hugo_convert_tojson/) - Convert front matter to JSON
|
||||
* [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 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo convert toJSON"
|
||||
slug: hugo_convert_toJSON
|
||||
url: /commands/hugo_convert_tojson/
|
||||
@@ -10,7 +10,6 @@ Convert front matter to JSON
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
toJSON converts all front matter in the content directory
|
||||
to use JSON for the front matter.
|
||||
|
||||
@@ -40,6 +39,7 @@ hugo convert toJSON [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo convert toTOML"
|
||||
slug: hugo_convert_toTOML
|
||||
url: /commands/hugo_convert_totoml/
|
||||
@@ -10,7 +10,6 @@ Convert front matter to TOML
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
toTOML converts all front matter in the content directory
|
||||
to use TOML for the front matter.
|
||||
|
||||
@@ -40,6 +39,7 @@ hugo convert toTOML [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo convert toYAML"
|
||||
slug: hugo_convert_toYAML
|
||||
url: /commands/hugo_convert_toyaml/
|
||||
@@ -10,7 +10,6 @@ Convert front matter to YAML
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
toYAML converts all front matter in the content directory
|
||||
to use YAML for the front matter.
|
||||
|
||||
@@ -40,6 +39,7 @@ hugo convert toYAML [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo convert](/commands/hugo_convert/) - Convert your content to different formats
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo env"
|
||||
slug: hugo_env
|
||||
url: /commands/hugo_env/
|
||||
@@ -10,7 +10,6 @@ Print Hugo version and environment info
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Print Hugo version and environment info. This is useful in Hugo bug reports.
|
||||
|
||||
```
|
||||
@@ -36,6 +35,7 @@ hugo env [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo gen"
|
||||
slug: hugo_gen
|
||||
url: /commands/hugo_gen/
|
||||
@@ -10,7 +10,6 @@ A collection of several useful generators.
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
A collection of several useful generators.
|
||||
|
||||
### Options
|
||||
@@ -32,10 +31,11 @@ A collection of several useful generators.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
* [hugo gen autocomplete](/commands/hugo_gen_autocomplete/) - Generate shell autocompletion script for Hugo
|
||||
* [hugo gen chromastyles](/commands/hugo_gen_chromastyles/) - Generate CSS stylesheet for the Chroma code highlighter
|
||||
* [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 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo gen autocomplete"
|
||||
slug: hugo_gen_autocomplete
|
||||
url: /commands/hugo_gen_autocomplete/
|
||||
@@ -10,7 +10,6 @@ Generate shell autocompletion script for Hugo
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Generates a shell autocompletion script for Hugo.
|
||||
|
||||
NOTE: The current version supports Bash only.
|
||||
@@ -54,6 +53,7 @@ hugo gen autocomplete [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo gen chromastyles"
|
||||
slug: hugo_gen_chromastyles
|
||||
url: /commands/hugo_gen_chromastyles/
|
||||
@@ -10,7 +10,6 @@ Generate CSS stylesheet for the Chroma code highlighter
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Generate CSS stylesheet for the Chroma code highlighter for a given style. This stylesheet is needed if pygmentsUseClasses is enabled in config.
|
||||
|
||||
See https://help.farbox.com/pygments.html for preview of available styles
|
||||
@@ -41,6 +40,7 @@ hugo gen chromastyles [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo gen doc"
|
||||
slug: hugo_gen_doc
|
||||
url: /commands/hugo_gen_doc/
|
||||
@@ -10,7 +10,6 @@ Generate Markdown documentation for the Hugo CLI.
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Generate Markdown documentation for the Hugo CLI.
|
||||
|
||||
This command is, mostly, used to create up-to-date documentation
|
||||
@@ -43,6 +42,7 @@ hugo gen doc [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo gen man"
|
||||
slug: hugo_gen_man
|
||||
url: /commands/hugo_gen_man/
|
||||
@@ -10,7 +10,6 @@ Generate man pages for the Hugo CLI
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
This command automatically generates up-to-date man pages of Hugo's
|
||||
command-line interface. By default, it creates the man page files
|
||||
in the "man" directory under the current directory.
|
||||
@@ -39,6 +38,7 @@ hugo gen man [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo gen](/commands/hugo_gen/) - A collection of several useful generators.
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo import"
|
||||
slug: hugo_import
|
||||
url: /commands/hugo_import/
|
||||
@@ -10,7 +10,6 @@ Import your site from others.
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Import your site from other web site generators like Jekyll.
|
||||
|
||||
Import requires a subcommand, e.g. `hugo import jekyll jekyll_root_path target_path`.
|
||||
@@ -34,7 +33,8 @@ Import requires a subcommand, e.g. `hugo import jekyll jekyll_root_path target_p
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
* [hugo import jekyll](/commands/hugo_import_jekyll/) - hugo import from Jekyll
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo import jekyll"
|
||||
slug: hugo_import_jekyll
|
||||
url: /commands/hugo_import_jekyll/
|
||||
@@ -10,7 +10,6 @@ hugo import from Jekyll
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
hugo import from Jekyll.
|
||||
|
||||
Import from Jekyll requires two paths, e.g. `hugo import jekyll jekyll_root_path target_path`.
|
||||
@@ -39,6 +38,7 @@ hugo import jekyll [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo import](/commands/hugo_import/) - Import your site from others.
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo list"
|
||||
slug: hugo_list
|
||||
url: /commands/hugo_list/
|
||||
@@ -10,7 +10,6 @@ Listing out various types of content
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Listing out various types of content.
|
||||
|
||||
List requires a subcommand, e.g. `hugo list drafts`.
|
||||
@@ -35,9 +34,10 @@ List requires a subcommand, e.g. `hugo list drafts`.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
* [hugo list drafts](/commands/hugo_list_drafts/) - List all 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 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo list drafts"
|
||||
slug: hugo_list_drafts
|
||||
url: /commands/hugo_list_drafts/
|
||||
@@ -10,7 +10,6 @@ List all drafts
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
List all of the drafts in your content directory.
|
||||
|
||||
```
|
||||
@@ -37,6 +36,7 @@ hugo list drafts [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo list](/commands/hugo_list/) - Listing out various types of content
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo list expired"
|
||||
slug: hugo_list_expired
|
||||
url: /commands/hugo_list_expired/
|
||||
@@ -10,7 +10,6 @@ List all posts already expired
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
List all of the posts in your content directory which has already
|
||||
expired.
|
||||
|
||||
@@ -38,6 +37,7 @@ hugo list expired [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo list](/commands/hugo_list/) - Listing out various types of content
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo list future"
|
||||
slug: hugo_list_future
|
||||
url: /commands/hugo_list_future/
|
||||
@@ -10,7 +10,6 @@ List all posts dated in the future
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
List all of the posts in your content directory which will be
|
||||
posted in the future.
|
||||
|
||||
@@ -38,6 +37,7 @@ hugo list future [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo list](/commands/hugo_list/) - Listing out various types of content
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo new"
|
||||
slug: hugo_new
|
||||
url: /commands/hugo_new/
|
||||
@@ -10,7 +10,6 @@ Create new content for your site
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Create a new content file and automatically set the date and title.
|
||||
It will guess which kind of file to create based on the path provided.
|
||||
|
||||
@@ -44,8 +43,9 @@ hugo new [path] [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
* [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 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo new site"
|
||||
slug: hugo_new_site
|
||||
url: /commands/hugo_new_site/
|
||||
@@ -10,7 +10,6 @@ Create a new site (skeleton)
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Create a new site in the provided directory.
|
||||
The new site will have the correct structure, but no content or theme yet.
|
||||
Use `hugo new [contentPath]` to create new content.
|
||||
@@ -41,6 +40,7 @@ hugo new site [path] [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo new](/commands/hugo_new/) - Create new content for your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo new theme"
|
||||
slug: hugo_new_theme
|
||||
url: /commands/hugo_new_theme/
|
||||
@@ -10,7 +10,6 @@ Create a new theme
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Create a new theme (skeleton) called [name] in the current directory.
|
||||
New theme is a skeleton. Please add content to the touched files. Add your
|
||||
name to the copyright line in the license and adjust the theme.toml file
|
||||
@@ -40,6 +39,7 @@ hugo new theme [name] [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo new](/commands/hugo_new/) - Create new content for your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo server"
|
||||
slug: hugo_server
|
||||
url: /commands/hugo_server/
|
||||
@@ -10,7 +10,6 @@ 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
|
||||
@@ -38,16 +37,13 @@ hugo server [flags]
|
||||
-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 if true, all relative URLs will be canonicalized using baseURL
|
||||
--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
|
||||
--disable404 do not render 404 page
|
||||
--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
|
||||
--disableRSS do not build RSS files
|
||||
--disableSitemap do not build Sitemap file
|
||||
--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
|
||||
@@ -62,9 +58,9 @@ hugo server [flags]
|
||||
--noChmod don't sync permission mode of files
|
||||
--noHTTPCache prevent HTTP caching
|
||||
--noTimes don't sync modification time of files
|
||||
--pluralizeListTitles pluralize titles in lists using inflect (default true)
|
||||
--pluralizeListTitles (deprecated) pluralize titles in lists using inflect (default true)
|
||||
-p, --port int port on which the server will listen (default 1313)
|
||||
--preserveTaxonomyNames preserve taxonomy names as written ("Gérard Depardieu" vs "gerard-depardieu")
|
||||
--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
|
||||
@@ -72,7 +68,7 @@ hugo server [flags]
|
||||
--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 if true, use /filename.html instead of /filename/
|
||||
--uglyURLs (deprecated) if true, use /filename.html instead of /filename/
|
||||
-w, --watch watch filesystem for changes and recreate as needed (default true)
|
||||
```
|
||||
|
||||
@@ -89,6 +85,7 @@ hugo server [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
title: "hugo undraft"
|
||||
slug: hugo_undraft
|
||||
url: /commands/hugo_undraft/
|
||||
---
|
||||
## hugo undraft
|
||||
|
||||
Undraft resets the content's draft status
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Undraft resets the content's draft status
|
||||
and updates the date to the current date and time.
|
||||
If the content's draft status is 'False', nothing is done.
|
||||
|
||||
```
|
||||
hugo undraft path/to/content [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for undraft
|
||||
```
|
||||
|
||||
### 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 28-Dec-2017
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
date: 2017-12-28T18:49:29+01:00
|
||||
date: 2018-01-31T11:40:21+01:00
|
||||
title: "hugo version"
|
||||
slug: hugo_version
|
||||
url: /commands/hugo_version/
|
||||
@@ -10,7 +10,6 @@ Print the version number of Hugo
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
All software has versions. This is Hugo's.
|
||||
|
||||
```
|
||||
@@ -36,6 +35,7 @@ hugo version [flags]
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [hugo](/commands/hugo/) - hugo builds your site
|
||||
|
||||
###### Auto generated by spf13/cobra on 28-Dec-2017
|
||||
###### Auto generated by spf13/cobra on 31-Jan-2018
|
||||
|
||||
@@ -17,4 +17,4 @@ aliases: [/content/,/content/organization]
|
||||
toc: false
|
||||
---
|
||||
|
||||
A static site generator needs to extend beyond front matter and a couple templates to be both scalable and *manageable*. Hugo was designed with not only developers in mind, but also content managers and authors.
|
||||
A static site generator needs to extend beyond front matter and a couple of templates to be both scalable and *manageable*. Hugo was designed with not only developers in mind, but also content managers and authors.
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
title: Archetypes
|
||||
linktitle: Archetypes
|
||||
description: Archetypes allow you to create new instances of content types and set default parameters from the command line.
|
||||
description: Archetypes are templates used when creating new content.
|
||||
date: 2017-02-01
|
||||
publishdate: 2017-02-01
|
||||
lastmod: 2017-02-01
|
||||
keywords: [archetypes,generators,metadata,front matter]
|
||||
categories: ["content management"]
|
||||
menu:
|
||||
@@ -18,175 +17,59 @@ aliases: [/content/archetypes/]
|
||||
toc: true
|
||||
---
|
||||
|
||||
{{% note %}}
|
||||
This section is outdated, see https://github.com/gohugoio/hugoDocs/issues/11
|
||||
{{% /note %}}
|
||||
{{% todo %}}
|
||||
See above
|
||||
{{% /todo %}}
|
||||
|
||||
## What are Archetypes?
|
||||
|
||||
**Archetypes** are content files in the [archetypes directory][] of your project that contain preconfigured [front matter][] for your website's [content types][]. Archetypes facilitate consistent metadata across your website content and allow content authors to quickly generate instances of a content type via the `hugo new` command.
|
||||
**Archetypes** are content template files in the [archetypes directory][] of your project that contain preconfigured [front matter][] and possibly also a content disposition for your website's [content types][]. These will be used when you run `hugo new`.
|
||||
|
||||
{{< youtube bcme8AzVh6o >}}
|
||||
|
||||
The `hugo new` generator for archetypes assumes your working directory is the content folder at the root of your project. Hugo is able to infer the appropriate archetype by assuming the content type from the content section passed to the CLI command:
|
||||
|
||||
```
|
||||
hugo new <content-section>/<file-name.md>
|
||||
```
|
||||
|
||||
We can use this pattern to create a new `.md` file in the `posts` section:
|
||||
The `hugo new` uses the `content-section` to find the most suitable archetype template in your project. If your project does not contain any archetype files, it will also look in the theme.
|
||||
|
||||
{{< code file="archetype-example.sh" >}}
|
||||
hugo new posts/my-first-post.md
|
||||
{{< /code >}}
|
||||
|
||||
{{% note "Override Content Type in a New File" %}}
|
||||
To override the content type Hugo infers from `[content-section]`, add the `--kind` flag to the end of the `hugo new` command.
|
||||
{{% /note %}}
|
||||
|
||||
Running this command in a new site that does not have default or custom archetypes will create the following file:
|
||||
|
||||
{{< output file="content/posts/my-first-post.md" >}}
|
||||
+++
|
||||
date = "2017-02-01T19:20:04-07:00"
|
||||
title = "my first post"
|
||||
draft = true
|
||||
+++
|
||||
{{< /output >}}
|
||||
|
||||
{{% note %}}
|
||||
In this example, if you do not already have a `content/posts` directory, Hugo will create both `content/posts/` and `content/posts/my-first-post.md` for you.
|
||||
{{% /note %}}
|
||||
|
||||
The auto-populated fields are worth examining:
|
||||
|
||||
* `title` is generated from the new content's filename (i.e. in this case, `my-first-post` becomes `"my first post"`)
|
||||
* `date` and `title` are the variables that ship with Hugo and are therefore included in *all* content files created with the Hugo CLI. `date` is generated in [RFC 3339 format][] by way of Go's [`now()`][] function, which returns the current time.
|
||||
* The third variable, `draft = true`, is *not* inherited by your default or custom archetypes but is included in Hugo's automatically scaffolded `default.md` archetype for convenience.
|
||||
|
||||
Three variables per content file are often not enough for effective content management of larger websites. Luckily, Hugo provides a simple mechanism for extending the number of variables through custom archetypes, as well as default archetypes to keep content creation DRY.
|
||||
|
||||
## Lookup Order for Archetypes
|
||||
|
||||
Similar to the [lookup order for templates][lookup] in your `layouts` directory, Hugo looks for a section- or type-specific archetype, then a default archetype, and finally an internal archetype that ships with Hugo. For example, Hugo will look for an archetype for `content/posts/my-first-post.md` in the following order:
|
||||
The above will create a new content file in `content/posts/my-first-post.md` using the first archetype file found of these:
|
||||
|
||||
1. `archetypes/posts.md`
|
||||
2. `archetypes/default.md`
|
||||
3. `themes/<THEME>/archetypes/posts.md`
|
||||
4. `themes/<THEME>/archetypes/default.md` (Auto-generated with `hugo new site`)
|
||||
3. `themes/my-theme/posts.md`
|
||||
4. `themes/my-theme/default.md`
|
||||
|
||||
{{% note "Using a Theme Archetype" %}}
|
||||
If you wish to use archetypes that ship with a theme, the `theme` field must be specified in your [configuration file](/getting-started/configuration/).
|
||||
{{% /note %}}
|
||||
The last two list items is only applicable if you use a theme and it uses the `my-theme` theme name as an example.
|
||||
|
||||
## Choose Your Archetype's Front Matter Format
|
||||
## Create a New Archetype Template
|
||||
|
||||
By default, `hugo new` content files include front matter in the TOML format regardless of the format used in `archetypes/*.md`.
|
||||
A fictional example for the section `newsletter` and the archetype file `archetypes/newsletter.md`. Create a new file in `archetypes/newsletter.md` and open it in a text editor.
|
||||
|
||||
You can specify a different default format in your site [configuration file][] file using the `metaDataFormat` directive. Possible values are `toml`, `yaml`, and `json`.
|
||||
{{< code file="archetypes/newsletter.md" >}}
|
||||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
||||
## Default Archetypes
|
||||
**Insert Lead paragraph here.**
|
||||
|
||||
Default archetypes are convenient if your content's front matter stays consistent across multiple [content sections][sections].
|
||||
## New Cool Posts
|
||||
|
||||
### Create the Default Archetype
|
||||
|
||||
When you create a new Hugo project using `hugo new site`, you'll notice that Hugo has already scaffolded a file at `archetypes/default.md`.
|
||||
|
||||
The following examples are from a site that's using `tags` and `categories` as [taxonomies][]. If we assume that all content files will require these two key-values, we can create a `default.md` archetype that *extends* Hugo's base archetype. In this example, we are including "golang" and "hugo" as tags and "web development" as a category.
|
||||
|
||||
{{< code file="archetypes/default.md" >}}
|
||||
+++
|
||||
tags = ["golang", "hugo"]
|
||||
categories = ["web development"]
|
||||
+++
|
||||
{{ range first 10 ( where .Site.RegularPages "Type" "cool" ) }}
|
||||
* {{ .Title }}
|
||||
{{ end }}
|
||||
{{< /code >}}
|
||||
|
||||
{{% warning "EOL Characters in Text Editors"%}}
|
||||
If you get an `EOF error` when using `hugo new`, add a carriage return after the closing `+++` or `---` for your TOML or YAML front matter, respectively. (See the [troubleshooting article on EOF errors](/troubleshooting/eof-error/) for more information.)
|
||||
{{% /warning %}}
|
||||
When you create a new newsletter with:
|
||||
|
||||
### Use the Default Archetype
|
||||
```bash
|
||||
hugo new newsletter/the-latest-cool.stuff.md
|
||||
```
|
||||
|
||||
With an `archetypes/default.md` in place, we can use the CLI to create a new post in the `posts` content section:
|
||||
It will create a new newsletter type of content file based on the archetype template.
|
||||
|
||||
{{< code file="new-post-from-default.sh" >}}
|
||||
$ hugo new posts/my-new-post.md
|
||||
{{< /code >}}
|
||||
**Note:** the site will only be built if the `.Site` is in use in the archetype file, and this can be time consuming for big sites.
|
||||
|
||||
Hugo then creates a new markdown file with the following front matter:
|
||||
The above _newsletter type archetype_ illustrates the possibilities: The full Hugo `.Site` and all of Hugo's template funcs can be used in the archetype file.
|
||||
|
||||
{{< output file="content/posts/my-new-post.md" >}}
|
||||
+++
|
||||
categories = ["web development"]
|
||||
date = "2017-02-01T19:20:04-07:00"
|
||||
tags = ["golang", "hugo"]
|
||||
title = "my new post"
|
||||
+++
|
||||
{{< /output >}}
|
||||
|
||||
We see that the `title` and `date` key-values have been added in addition to the `tags` and `categories` key-values from `archetypes/default.md`.
|
||||
|
||||
{{% note "Ordering of Front Matter" %}}
|
||||
You may notice that content files created with `hugo new` do not respect the order of the key-values specified in your archetype files. This is a [known issue](https://github.com/gohugoio/hugo/issues/452).
|
||||
{{% /note %}}
|
||||
|
||||
## Custom Archetypes
|
||||
|
||||
Suppose your site's `posts` section requires more sophisticated front matter than what has been specified in `archetypes/default.md`. You can create a custom archetype for your posts at `archetypes/posts.md` that includes the full set of front matter to be added to the two default archetypes fields.
|
||||
|
||||
### Create a Custom Archetype
|
||||
|
||||
{{< code file="archetypes/posts.md">}}
|
||||
+++
|
||||
description = ""
|
||||
tags = ""
|
||||
categories = ""
|
||||
+++
|
||||
{{< /code >}}
|
||||
|
||||
### Use a Custom Archetype
|
||||
|
||||
With an `archetypes/posts.md` in place, you can use the Hugo CLI to create a new post with your preconfigured front matter in the `posts` content section:
|
||||
|
||||
{{< code file="new-post-from-custom.sh" >}}
|
||||
$ hugo new posts/post-from-custom.md
|
||||
{{< /code >}}
|
||||
|
||||
This time, Hugo recognizes our custom `archetypes/posts.md` archetype and uses it instead of `archetypes/default.md`. The generated file will now include the full list of front matter parameters, as well as the base archetype's `title` and `date`:
|
||||
|
||||
{{< output file="content/posts/post-from-custom-archetype.md" >}}
|
||||
+++
|
||||
categories = ""
|
||||
date = 2017-02-13T17:24:43-08:00
|
||||
description = ""
|
||||
tags = ""
|
||||
title = "post from custom archetype"
|
||||
+++
|
||||
{{< /output >}}
|
||||
|
||||
### Hugo Docs Custom Archetype
|
||||
|
||||
As an example of archetypes in practice, the following is the `functions` archetype from the Hugo docs:
|
||||
|
||||
{{< code file="archetypes/functions.md" >}}
|
||||
{{< readfile file="/themes/gohugoioTheme/archetypes/functions.md" >}}
|
||||
{{< /code >}}
|
||||
|
||||
{{% note %}}
|
||||
The preceding archetype is kept up to date with every Hugo build by using Hugo's [`readFile` function](/functions/readfile/). For similar examples, see [Local File Templates](/templates/files/).
|
||||
{{% /note %}}
|
||||
|
||||
[archetypes directory]: /getting-started/directory-structure/
|
||||
[`now()`]: http://golang.org/pkg/time/#Now
|
||||
[configuration file]: /getting-started/configuration/
|
||||
[sections]: /content-management/sections/
|
||||
[content types]: /content-management/types/
|
||||
[front matter]: /content-management/front-matter/
|
||||
[RFC 3339 format]: https://www.ietf.org/rfc/rfc3339.txt
|
||||
[taxonomies]: /content-management/taxonomies/
|
||||
[lookup]: /templates/lookup/
|
||||
[templates]: /templates/
|
||||
|
||||
@@ -27,15 +27,11 @@ Hugo comes with all the code you need to load Disqus into your templates. Before
|
||||
|
||||
### Configure Disqus
|
||||
|
||||
Disqus comments require you set a single value in your [site's configuration file][configuration]. The following show the configuration variable in a `config.toml` and `config.yml`, respectively:
|
||||
Disqus comments require you set a single value in your [site's configuration file][configuration] like so:
|
||||
|
||||
```
|
||||
{{< code-toggle copy="false" >}}
|
||||
disqusShortname = "yourdiscussshortname"
|
||||
```
|
||||
|
||||
```
|
||||
disqusShortname: "yourdiscussshortname"
|
||||
```
|
||||
{{</ code-toggle >}}
|
||||
|
||||
For many websites, this is enough configuration. However, you also have the option to set the following in the [front matter][] of a single content file:
|
||||
|
||||
|
||||
@@ -61,13 +61,6 @@ To be sure to get the correct reference in this case, use the full path:
|
||||
{{</* relref "events/my-birthday.md" */>}} => /events/my-birthday/
|
||||
{{< /code >}}
|
||||
|
||||
{{< todo >}}Remove this warning when https://github.com/gohugoio/hugo/issues/3703 is released.{{< /todo >}}
|
||||
|
||||
A relative document name must *not* begin with a slash (`/`).
|
||||
```
|
||||
{{</* relref "/events/my-birthday.md" */>}} => ""
|
||||
```
|
||||
|
||||
### With Multiple Output Formats
|
||||
|
||||
If the page exists in multiple [output formats][], `ref` or `relref` can be used with a output format name:
|
||||
@@ -116,8 +109,22 @@ Ensuring heading uniqueness across the site is accomplished with a unique identi
|
||||
/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
|
||||
|
||||
@@ -84,7 +84,7 @@ See [Shortcodes][sc] for usage, particularly for the built-in shortcodes that sh
|
||||
|
||||
### Code Blocks
|
||||
|
||||
Hugo supports GitHub-flavored markdown's use of triple back ticks, as well as provides a special [`highlight` nested shortcode][hlsc] to render syntax highlighting via [Pygments][]. For usage examples and a complete explanation, see the [syntax highlighting documentation][hl] in [developer tools][].
|
||||
Hugo supports GitHub-flavored markdown's use of triple back ticks, as well as provides a special [`highlight` shortcode][hlsc], and syntax highlights those code blocks natively using *Chroma*. Users also have an option to use *Pygments* instead. See the [Syntax Highlighting][hl] section for details.
|
||||
|
||||
## Mmark
|
||||
|
||||
@@ -231,7 +231,7 @@ Markdown syntax is simple enough to learn in a single sitting. The following are
|
||||
[fireball]: https://daringfireball.net/projects/markdown/
|
||||
[gfmtasks]: https://guides.github.com/features/mastering-markdown/#syntax
|
||||
[helperssource]: https://github.com/gohugoio/hugo/blob/77c60a3440806067109347d04eb5368b65ea0fe8/helpers/general.go#L65
|
||||
[hl]: /tools/syntax-highlighting/
|
||||
[hl]: /content-management/syntax-highlighting/
|
||||
[hlsc]: /content-management/shortcodes/#highlight
|
||||
[hugocss]: /css/style.css
|
||||
[ietf]: https://tools.ietf.org/html/
|
||||
|
||||
@@ -34,9 +34,9 @@ YAML
|
||||
JSON
|
||||
: a single JSON object surrounded by '`{`' and '`}`', followed by a new line.
|
||||
|
||||
### TOML Example
|
||||
### Example
|
||||
|
||||
```
|
||||
{{< code-toggle >}}
|
||||
+++
|
||||
title = "spf13-vim 3.0 release and new website"
|
||||
description = "spf13-vim is a cross platform distribution of vim plugins and resources for Vim."
|
||||
@@ -48,39 +48,7 @@ categories = [
|
||||
]
|
||||
slug = "spf13-vim-3-0-release-and-new-website"
|
||||
+++
|
||||
```
|
||||
|
||||
### YAML Example
|
||||
|
||||
```
|
||||
---
|
||||
title: "spf13-vim 3.0 release and new website"
|
||||
description: "spf13-vim is a cross platform distribution of vim plugins and resources for Vim."
|
||||
tags: [ ".vimrc", "plugins", "spf13-vim", "vim" ]
|
||||
lastmod: 2015-12-23
|
||||
date: "2012-04-06"
|
||||
categories:
|
||||
- "Development"
|
||||
- "VIM"
|
||||
slug: "spf13-vim-3-0-release-and-new-website"
|
||||
---
|
||||
```
|
||||
|
||||
### JSON Example
|
||||
|
||||
```
|
||||
{
|
||||
"title": "spf13-vim 3.0 release and new website",
|
||||
"description": "spf13-vim is a cross platform distribution of vim plugins and resources for Vim.",
|
||||
"tags": [ ".vimrc", "plugins", "spf13-vim", "vim" ],
|
||||
"date": "2012-04-06",
|
||||
"categories": [
|
||||
"Development",
|
||||
"VIM"
|
||||
],
|
||||
"slug": "spf13-vim-3-0-release-and-new-website"
|
||||
}
|
||||
```
|
||||
{{</ code-toggle >}}
|
||||
|
||||
## Front Matter Variables
|
||||
|
||||
@@ -103,6 +71,9 @@ There are a few predefined variables that Hugo is aware of. See [Page Variables]
|
||||
`expiryDate`
|
||||
: the datetime at which the content should no longer be published by Hugo; expired content will not be rendered unless the `--buildExpired` flag is passed to the `hugo` command.
|
||||
|
||||
`headless`
|
||||
: if `true`, sets a leaf bundle to be [headless][headless-bundle].
|
||||
|
||||
`isCJKLanguage`
|
||||
: if `true`, Hugo will explicitly treat the content as a CJK language; both `.Summary` and `.WordCount` work properly in CJK languages.
|
||||
|
||||
@@ -127,6 +98,9 @@ There are a few predefined variables that Hugo is aware of. See [Page Variables]
|
||||
`publishDate`
|
||||
: if in the future, content will not be rendered unless the `--buildFuture` flag is passed to `hugo`.
|
||||
|
||||
`resources`
|
||||
: used for configuring page bundle resources. See [Page Resources][page-resources].
|
||||
|
||||
`slug`
|
||||
: appears as the tail of the output URL. A value specified in front matter will override the segment of the URL based on the filename.
|
||||
|
||||
@@ -161,8 +135,6 @@ include_toc: true
|
||||
show_comments: false
|
||||
```
|
||||
|
||||
These two user-defined fields can then be accessed via `.Params.include_toc` and `.Params.show_comments`, respectively. The [Variables][variables] section provides more information on using Hugo's page- and site-level variables in your templates.
|
||||
|
||||
|
||||
## Order Content Through Front Matter
|
||||
|
||||
@@ -186,11 +158,13 @@ It's possible to set some options for Markdown rendering in a content's front ma
|
||||
[content type]: /content-management/types/
|
||||
[contentorg]: /content-management/organization/
|
||||
[definetype]: /content-management/types/#defining-a-content-type "Learn how to specify a type and a layout in a content's front matter"
|
||||
[headless-bundle]: /content-management/page-bundles/#headless-bundle
|
||||
[json]: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf "Specification for JSON, JavaScript Object Notation"
|
||||
[lists]: /templates/lists/#ordering-content "See how to order content in list pages; for example, templates that look to specific _index.md for content and front matter."
|
||||
[lookup]: /templates/lookup-order/ "Hugo traverses your templates in a specific order when rendering content to allow for DRYer templating."
|
||||
[ordering]: /templates/lists/ "Hugo provides multiple ways to sort and order your content in list templates"
|
||||
[outputs]: /templates/output-formats/ "With the release of v22, you can output your content to any text format using Hugo's familiar templating"
|
||||
[page-resources]: /content-management/page-resources/
|
||||
[pagevars]: /variables/page/
|
||||
[section]: /content-management/sections/
|
||||
[taxweight]: /content-management/taxonomies/
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
---
|
||||
title: "Image Processing"
|
||||
description: "Image Page resources can be resized and cropped."
|
||||
date: 2018-01-24T13:10:00-05:00
|
||||
lastmod: 2018-01-26T15:59:07-05:00
|
||||
linktitle: "Image Processing"
|
||||
categories: ["content management"]
|
||||
keywords: [bundle,content,resources,images]
|
||||
weight: 4004
|
||||
draft: false
|
||||
toc: true
|
||||
menu:
|
||||
docs:
|
||||
parent: "content-management"
|
||||
weight: 32
|
||||
---
|
||||
|
||||
## The Image Page Resource
|
||||
|
||||
The `image` is a [Page Resource]({{< relref "content-management/page-resources" >}}), and the processing methods listed below does not work on images inside your `/static` folder.
|
||||
|
||||
|
||||
To get all images in a [Page Bundle]({{< relref "content-management/organization#page-bundles" >}}):
|
||||
|
||||
|
||||
```go-html-template
|
||||
{{ with .Resources.ByType "image" }}
|
||||
{{ end }}
|
||||
|
||||
```
|
||||
|
||||
## Image Processing Methods
|
||||
|
||||
|
||||
The `image` resource implements the methods `Resize`, `Fit` and `Fill`, each returning the transformed image using the specified dimensions and processing options.
|
||||
|
||||
Resize
|
||||
: Resizes the image to the specified width and height.
|
||||
|
||||
```go
|
||||
// Resize to a width of 600px and preserve ratio
|
||||
{{ $image := $resource.Resize "600x" }}
|
||||
|
||||
// Resize to a height of 400px and preserve ratio
|
||||
{{ $image := $resource.Resize "x400" }}
|
||||
|
||||
// Resize to a width 600px and a height of 400px
|
||||
{{ $image := $resource.Resize "600x400" }}
|
||||
```
|
||||
|
||||
Fit
|
||||
: Scale down the image to fit the given dimensions while maintaining aspect ratio. Both height and width are required.
|
||||
|
||||
```go
|
||||
{{ $image := $resource.Fit "600x400" }}
|
||||
```
|
||||
|
||||
Fill
|
||||
: Resize and crop the image to match the given dimensions. Both height and width are required.
|
||||
|
||||
```go
|
||||
{{ $image := $resource.Fill "600x400" }}
|
||||
```
|
||||
|
||||
|
||||
{{% note %}}
|
||||
Image operations in Hugo currently **do not preserve EXIF data** as this is not supported by Go's [image package](https://github.com/golang/go/search?q=exif&type=Issues&utf8=%E2%9C%93). This will be improved on in the future.
|
||||
{{% /note %}}
|
||||
|
||||
|
||||
## Image Processing Options
|
||||
|
||||
In addition to the dimensions (e.g. `600x400`), Hugo supports a set of additional image options.
|
||||
|
||||
|
||||
JPEG Quality
|
||||
: Only relevant for JPEG images, values 1 to 100 inclusive, higher is better. Default is 75.
|
||||
|
||||
```go
|
||||
{{ $image.Resize "600x q50" }}
|
||||
```
|
||||
|
||||
Rotate
|
||||
: Rotates an image by the given angle counter-clockwise. The rotation will be performed first to get the dimensions correct. The main use of this is to be able to manually correct for [EXIF orientation](https://github.com/golang/go/issues/4341) of JPEG images.
|
||||
|
||||
```go
|
||||
{{ $image.Resize "600x r90" }}
|
||||
```
|
||||
|
||||
Anchor
|
||||
: Only relevant for the `Fill` method. This is useful for thumbnail generation where the main motive is located in, say, the left corner.
|
||||
Valid are `Center`, `TopLeft`, `Top`, `TopRight`, `Left`, `Right`, `BottomLeft`, `Bottom`, `BottomRight`.
|
||||
|
||||
```go
|
||||
{{ $image.Fill "300x200 BottomLeft" }}
|
||||
```
|
||||
|
||||
Resample Filter
|
||||
: Filter used in resizing. Default is `Box`, a simple and fast resampling filter appropriate for downscaling.
|
||||
|
||||
Examples are: `Box`, `NearestNeighbor`, `Linear`, `Gaussian`.
|
||||
|
||||
See https://github.com/disintegration/imaging for more. If you want to trade quality for faster processing, this may be a option to test.
|
||||
|
||||
```go
|
||||
{{ $image.Resize "600x400 Gaussian" }}
|
||||
```
|
||||
|
||||
## Image Processing Examples
|
||||
|
||||
_The photo of the sunset used in the examples below is Copyright [Bjørn Erik Pedersen](https://commons.wikimedia.org/wiki/User:Bep) (Creative Commons Attribution-Share Alike 4.0 International license)_
|
||||
|
||||
|
||||
{{< imgproc sunset Resize "300x" />}}
|
||||
|
||||
{{< imgproc sunset Fill "90x120 left" />}}
|
||||
|
||||
{{< imgproc sunset Fill "90x120 right" />}}
|
||||
|
||||
{{< imgproc sunset Fit "90x90" />}}
|
||||
|
||||
{{< imgproc sunset Resize "300x q10" />}}
|
||||
|
||||
|
||||
This is the shortcode used in the examples above:
|
||||
|
||||
|
||||
{{< code file="layouts/shortcodes/imgproc.html" >}}
|
||||
{{< readfile file="layouts/shortcodes/imgproc.html" >}}
|
||||
{{< /code >}}
|
||||
|
||||
And it is used like this:
|
||||
|
||||
```go-html-template
|
||||
{{</* imgproc sunset Resize "300x" /*/>}}
|
||||
```
|
||||
|
||||
|
||||
{{% note %}}
|
||||
**Tip:** Note the self-closing shortcode syntax above. The `imgproc` shortcode can be called both with and without **inner content**.
|
||||
{{% /note %}}
|
||||
|
||||
## Image Processing Config
|
||||
|
||||
You can configure an `imaging` section in `config.toml` with default image processing options:
|
||||
|
||||
```toml
|
||||
[imaging]
|
||||
# Default resample filter used for resizing. Default is Box,
|
||||
# a simple and fast averaging filter appropriate for downscaling.
|
||||
# See https://github.com/disintegration/imaging
|
||||
resampleFilter = "box"
|
||||
|
||||
# Defatult JPEG quality setting. Default is 75.
|
||||
quality = 75
|
||||
|
||||
# Anchor used when cropping pictures.
|
||||
# Default is "smart" which does Smart Cropping, using https://github.com/muesli/smartcrop
|
||||
# Smart Cropping is content aware and tries to find the best crop for each image.
|
||||
# Valid values are Smart, Center, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight
|
||||
anchor = "smart"
|
||||
|
||||
```
|
||||
|
||||
All of the above settings can also be set per image procecssing.
|
||||
|
||||
## Smart Cropping of Images
|
||||
|
||||
By default, Hugo will use the [Smartcrop](https://github.com/muesli/smartcrop), a library created by [muesli](https://github.com/muesli), when cropping images with `.Fill`. You can set the anchor point manually, but in most cases the smart option will make a good choice. And we will work with the libray author to improve this in the future.
|
||||
|
||||
An example using the sunset image from above:
|
||||
|
||||
|
||||
{{< imgproc sunset Fill "200x200 smart" />}}
|
||||
|
||||
|
||||
## Image Processing Performance Consideration
|
||||
|
||||
Processed images are stored below `<project-dir>/resources` (can be set with `resourceDir` config setting). This folder is deliberately placed in the project, as it is recommended to check these into source control as part of the project. These images are not "Hugo fast" to generate, but once generated they can be reused.
|
||||
|
||||
If you change your image settings (e.g. size), remove or rename images etc., you will end up with unused images taking up space and cluttering your project.
|
||||
|
||||
To clean up, run:
|
||||
|
||||
```bash
|
||||
hugo --gc
|
||||
```
|
||||
|
||||
|
||||
{{% note %}}
|
||||
**GC** is short for **Garbage Collection**.
|
||||
{{% /note %}}
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
@@ -118,9 +118,9 @@ menu:
|
||||
|
||||
You can also add entries to menus that aren’t attached to a piece of content. This takes place in your Hugo project's [`config` file][config].
|
||||
|
||||
Here’s an example snippet pulled from a `config.toml`:
|
||||
Here’s an example snippet pulled from a configuration file:
|
||||
|
||||
{{< code file="config.toml" >}}
|
||||
{{< code-toggle file="config.toml" >}}
|
||||
[[menu.main]]
|
||||
name = "about hugo"
|
||||
pre = "<i class='fa fa-heart'></i>"
|
||||
@@ -132,28 +132,10 @@ Here’s an example snippet pulled from a `config.toml`:
|
||||
pre = "<i class='fa fa-road'></i>"
|
||||
weight = -100
|
||||
url = "/getting-started/"
|
||||
{{< /code >}}
|
||||
|
||||
Here's the equivalent snippet in a `config.yaml`:
|
||||
|
||||
{{< code file="config.yml" >}}
|
||||
---
|
||||
menu:
|
||||
docs:
|
||||
- Name: "about hugo"
|
||||
Pre: "<i class='fa fa-heart'></i>"
|
||||
Weight: -110
|
||||
Identifier: "about"
|
||||
URL: "/about/"
|
||||
- Name: "getting started"
|
||||
Pre: "<i class='fa fa-road'></i>"
|
||||
Weight: -100
|
||||
URL: "/getting-started/"
|
||||
---
|
||||
{{< /code >}}
|
||||
{{< /code-toggle >}}
|
||||
|
||||
{{% note %}}
|
||||
The URLs must be relative to the context root. If the `baseURL` is `https://example.com/mysite/`, then the URLs in the menu must not include the context root `mysite`. Using an absolute URL will overide the baseURL. If the value used for `URL` in the above example is `https://subdomain.example.com/`, the output will be `https://subdomain.example.com`.
|
||||
The URLs must be relative to the context root. If the `baseURL` is `https://example.com/mysite/`, then the URLs in the menu must not include the context root `mysite`. Using an absolute URL will override the baseURL. If the value used for `URL` in the above example is `https://subdomain.example.com/`, the output will be `https://subdomain.example.com`.
|
||||
{{% /note %}}
|
||||
|
||||
## Nesting
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user