Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 76a5e1880a | |||
| 0bb337b22a | |||
| 03dc9170b3 | |||
| c829b736cc | |||
| 995a2159e5 | |||
| f88f0a9f59 | |||
| dd3f2731e0 | |||
| f772998fa3 | |||
| 8a55df7af2 | |||
| 52c9bd7908 | |||
| 44da086082 | |||
| 2ffaf1fc1e | |||
| a808f6e40d | |||
| 94f3908ec6 | |||
| 75fcc7524a | |||
| 33d1f2c853 | |||
| 64da6d7c62 | |||
| 70db201ed4 | |||
| 8a468df065 | |||
| b5fa03d3aa | |||
| 9da472dd3b | |||
| 615e45d6e8 | |||
| 635532a20f | |||
| a243a6154c | |||
| 9c71f600bf | |||
| 420527fc6b | |||
| 7fe786e3b0 | |||
| 03b244fc0b | |||
| 961181334f | |||
| e35b7f049d | |||
| 7d90277a28 | |||
| 0796fa7ace | |||
| 6bf15241a1 | |||
| 861ede6d10 | |||
| f228c87d41 | |||
| 1b701b72ca | |||
| a32d70b712 | |||
| 948cfb98f9 | |||
| 8930802ef2 | |||
| 7df45f615a | |||
| f961093ea9 | |||
| 89b8c32200 | |||
| 984358f047 | |||
| d1f191c58d | |||
| a198116669 | |||
| ce2470e701 | |||
| 921db7b52c | |||
| 786ce71e59 | |||
| 5ad2846161 | |||
| 36ad9f583a | |||
| 7c0a0bc979 | |||
| d83ce27ae0 | |||
| 5a5f4a5495 | |||
| c6acc246ab | |||
| 29ed932513 | |||
| 7b4ddd1863 | |||
| 671897ae91 | |||
| a879ebfaaa | |||
| 499794d19e | |||
| feb3d494b9 | |||
| 65c82178b7 | |||
| 332d5ec823 | |||
| 212cc11ada | |||
| dfb35dcd7a | |||
| ef053faa6a | |||
| af91eb1997 | |||
| a5ec542393 | |||
| 884439b9a2 | |||
| 128fb17c2b | |||
| e46d37a984 | |||
| 790a8aa474 | |||
| fe06735214 | |||
| d15baf53a9 |
@@ -4,7 +4,7 @@ parameters:
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.22600.20300
|
||||
- image: bepsays/ci-hugoreleaser:1.22600.20500
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22600.20300
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22600.20500
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
have_fun: false
|
||||
memory_config:
|
||||
disabled: false
|
||||
code_review:
|
||||
disable: false
|
||||
comment_severity_threshold: HIGH
|
||||
max_review_comments: -1
|
||||
pull_request_opened:
|
||||
help: true
|
||||
summary: false
|
||||
code_review: false
|
||||
include_drafts: false
|
||||
ignore_patterns: []
|
||||
@@ -51,6 +51,7 @@ jobs:
|
||||
run: |
|
||||
gem install asciidoctor -v "2.0.26"
|
||||
gem install asciidoctor-diagram -v "3.1.0"
|
||||
gem install asciidoctor-html5s -v "0.5.1"
|
||||
- name: Install GoAT
|
||||
run: go install github.com/blampe/goat/cmd/goat@177de93b192b8ffae608e5d9ec421cc99bf68402
|
||||
- name: Install Python
|
||||
@@ -63,7 +64,7 @@ jobs:
|
||||
run: go install github.com/gohugoio/gotmplfmt@latest
|
||||
- name: Install docutils
|
||||
run: |
|
||||
pip install docutils
|
||||
pip install docutils Pygments
|
||||
rst2html --version
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install pandoc on Linux
|
||||
|
||||
@@ -5,4 +5,5 @@ dist/
|
||||
public/
|
||||
.DS_Store
|
||||
cache/filecache/_gen/
|
||||
.claude/
|
||||
.claude/
|
||||
dump.txt
|
||||
@@ -12,6 +12,7 @@
|
||||
* In tests, use `qt` matchers (e.g. `b.Assert(err, qt.ErrorMatches, ...)`) instead of raw `if`/`t.Fatal` checks.
|
||||
* In tests, always use the latest Hugo specification, e.g. for layouts, it's `layouts/page.html` and not `layouts/_default/single.html`, `layouts/list.html` and not `layouts/_default/list.html`
|
||||
* Never name tests `TestIssue1234`; always give the test function a descriptive name, e.g. `TestDisablePathToLower`, and add any issue reference as a Go doc function comment, e.g. `// See issue 1234.`.
|
||||
* If you borrow a test case (e.g. from the issue), that test's author must be added as co-author in the commit.
|
||||
* If you're a security researcher, read @SECURITY.md carefully.
|
||||
* Brevity is good. This applies to code, comments and commit messages. Don't write a novel.
|
||||
* Use `./check.sh ./somepackage/...` when iterating.
|
||||
|
||||
@@ -72,6 +72,7 @@ To make the contribution process as seamless as possible, we ask for the followi
|
||||
* When you’re ready to create a pull request, be sure to:
|
||||
* Sign the [CLA](https://cla-assistant.io/gohugoio/hugo).
|
||||
* Have test cases for the new code. If you have questions about how to do this, please ask in your pull request.
|
||||
* If you borrow a test case (e.g. from the issue), that test's author must be added as [co-author](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors) in the commit.
|
||||
* Run `go fmt`.
|
||||
* Add documentation if you are adding new features or changing functionality. The docs site lives in `/docs`.
|
||||
* Squash your commits into a single commit. `git rebase -i`. It’s okay to force update your pull request with `git push -f`.
|
||||
|
||||
@@ -37,7 +37,6 @@ A fast and flexible static site generator built with love by [bep][], [spf13][],
|
||||
|
||||
[](https://godoc.org/github.com/gohugoio/hugo)
|
||||
[](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
|
||||
[](https://goreportcard.com/report/github.com/gohugoio/hugo)
|
||||
|
||||
[Website][] | [Installation][] | [Documentation][] | [Support][] | [Contributing][] | <a rel="me" href="https://fosstodon.org/@gohugoio">Mastodon</a>
|
||||
|
||||
@@ -139,10 +138,6 @@ To build and install the extended/deploy edition, first install a C compiler suc
|
||||
CGO_ENABLED=1 go install -tags extended,withdeploy github.com/gohugoio/hugo@latest
|
||||
```
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#gohugoio/hugo&Timeline)
|
||||
|
||||
## Documentation
|
||||
|
||||
Hugo's [documentation][] includes installation instructions, a quick start guide, conceptual explanations, reference information, and examples.
|
||||
@@ -195,10 +190,48 @@ Hugo stands on the shoulders of great open source libraries. Run `hugo env --log
|
||||
<summary>See current dependencies</summary>
|
||||
|
||||
```text
|
||||
cel.dev/expr="v0.25.1"
|
||||
cloud.google.com/go/auth/oauth2adapt="v0.2.8"
|
||||
cloud.google.com/go/auth="v0.20.0"
|
||||
cloud.google.com/go/compute/metadata="v0.9.0"
|
||||
cloud.google.com/go/iam="v1.5.3"
|
||||
cloud.google.com/go/monitoring="v1.24.3"
|
||||
cloud.google.com/go/storage="v1.57.2"
|
||||
cloud.google.com/go="v0.123.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore="v1.20.0"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity="v1.13.1"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal="v1.11.2"
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob="v1.6.3"
|
||||
github.com/Azure/go-autorest/autorest/to="v0.4.1"
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go="v1.6.0"
|
||||
github.com/BurntSushi/locker="v0.0.0-20171006230638-a6e239ea1c69"
|
||||
github.com/JohannesKaufmann/dom="v0.2.0"
|
||||
github.com/JohannesKaufmann/html-to-markdown/v2="v2.5.0"
|
||||
github.com/alecthomas/chroma/v2="v2.21.1"
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp="v1.31.0"
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric="v0.54.0"
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping="v0.54.0"
|
||||
github.com/JohannesKaufmann/dom="v0.3.1"
|
||||
github.com/JohannesKaufmann/html-to-markdown/v2="v2.5.2"
|
||||
github.com/alecthomas/chroma/v2="v2.27.0"
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream="v1.7.8"
|
||||
github.com/aws/aws-sdk-go-v2/config="v1.32.2"
|
||||
github.com/aws/aws-sdk-go-v2/credentials="v1.19.2"
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds="v1.18.14"
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager="v1.20.12"
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources="v1.4.22"
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2="v2.7.22"
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini="v1.8.4"
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a="v1.4.22"
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront="v1.61.1"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding="v1.13.7"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum="v1.9.13"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url="v1.13.21"
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared="v1.19.21"
|
||||
github.com/aws/aws-sdk-go-v2/service/s3="v1.97.3"
|
||||
github.com/aws/aws-sdk-go-v2/service/signin="v1.0.2"
|
||||
github.com/aws/aws-sdk-go-v2/service/sso="v1.30.5"
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc="v1.35.10"
|
||||
github.com/aws/aws-sdk-go-v2/service/sts="v1.41.2"
|
||||
github.com/aws/aws-sdk-go-v2="v1.41.6"
|
||||
github.com/aws/smithy-go="v1.25.0"
|
||||
github.com/aymerick/douceur="v0.2.0"
|
||||
github.com/bep/clocks="v0.5.0"
|
||||
github.com/bep/debounce="v1.2.0"
|
||||
@@ -206,105 +239,129 @@ github.com/bep/gitmap="v1.9.0"
|
||||
github.com/bep/goat="v0.5.0"
|
||||
github.com/bep/godartsass/v2="v2.5.0"
|
||||
github.com/bep/golibsass="v1.2.0"
|
||||
github.com/bep/goportabletext="v0.1.0"
|
||||
github.com/bep/helpers="v0.6.0"
|
||||
github.com/bep/imagemeta="v0.12.0"
|
||||
github.com/bep/lazycache="v0.8.0"
|
||||
github.com/bep/golocales="v0.2.0"
|
||||
github.com/bep/goportabletext="v0.2.0"
|
||||
github.com/bep/helpers="v0.12.0"
|
||||
github.com/bep/imagemeta="v0.17.2"
|
||||
github.com/bep/lazycache="v0.8.1"
|
||||
github.com/bep/logg="v0.4.0"
|
||||
github.com/bep/mclib="v1.20400.20402"
|
||||
github.com/bep/overlayfs="v0.10.0"
|
||||
github.com/bep/simplecobra="v0.6.1"
|
||||
github.com/bep/textandbinarywriter="v0.0.0-20251212174530-cd9f0732f60f"
|
||||
github.com/bep/tmc="v0.5.1"
|
||||
github.com/bits-and-blooms/bitset="v1.24.4"
|
||||
github.com/bep/mclib="v1.20401.20400"
|
||||
github.com/bep/overlayfs="v0.11.0"
|
||||
github.com/bep/simplecobra="v0.7.0"
|
||||
github.com/bep/textandbinarywriter="v0.1.0"
|
||||
github.com/bep/tmc="v0.6.0"
|
||||
github.com/bits-and-blooms/bitset="v1.24.5"
|
||||
github.com/cespare/xxhash/v2="v2.3.0"
|
||||
github.com/clbanning/mxj/v2="v2.7.0"
|
||||
github.com/clipperhouse/displaywidth="v0.6.0"
|
||||
github.com/clipperhouse/stringish="v0.1.1"
|
||||
github.com/clipperhouse/uax29/v2="v2.3.0"
|
||||
github.com/clipperhouse/displaywidth="v0.10.0"
|
||||
github.com/clipperhouse/uax29/v2="v2.6.0"
|
||||
github.com/cncf/xds/go="v0.0.0-20251210132809-ee656c7534f5"
|
||||
github.com/cpuguy83/go-md2man/v2="v2.0.6"
|
||||
github.com/disintegration/gift="v1.2.1"
|
||||
github.com/dlclark/regexp2="v1.11.5"
|
||||
github.com/evanw/esbuild="v0.27.2"
|
||||
github.com/dlclark/regexp2/v2="v2.2.1"
|
||||
github.com/dustin/go-humanize="v1.0.1"
|
||||
github.com/envoyproxy/go-control-plane/envoy="v1.36.0"
|
||||
github.com/envoyproxy/protoc-gen-validate="v1.3.0"
|
||||
github.com/evanw/esbuild="v0.28.1"
|
||||
github.com/fatih/color="v1.18.0"
|
||||
github.com/felixge/httpsnoop="v1.0.4"
|
||||
github.com/frankban/quicktest="v1.14.6"
|
||||
github.com/fsnotify/fsnotify="v1.9.0"
|
||||
github.com/getkin/kin-openapi="v0.133.0"
|
||||
github.com/go-openapi/jsonpointer="v0.21.0"
|
||||
github.com/go-openapi/swag="v0.23.0"
|
||||
github.com/getkin/kin-openapi="v0.140.0"
|
||||
github.com/go-jose/go-jose/v4="v4.1.4"
|
||||
github.com/go-logr/logr="v1.4.3"
|
||||
github.com/go-logr/stdr="v1.2.2"
|
||||
github.com/go-openapi/jsonpointer="v0.22.5"
|
||||
github.com/go-openapi/swag/jsonname="v0.25.5"
|
||||
github.com/gobuffalo/flect="v1.0.3"
|
||||
github.com/gobwas/glob="v0.2.3"
|
||||
github.com/goccy/go-yaml="v1.19.1"
|
||||
github.com/goccy/go-yaml="v1.19.2"
|
||||
github.com/gohugoio/gift="v0.2.0"
|
||||
github.com/gohugoio/go-i18n/v2="v2.1.3-0.20251018145728-cfcc22d823c6"
|
||||
github.com/gohugoio/go-radix="v1.2.0"
|
||||
github.com/gohugoio/hashstructure="v0.6.0"
|
||||
github.com/gohugoio/httpcache="v0.8.0"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras="v0.5.0"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.3.1"
|
||||
github.com/gohugoio/locales="v0.14.0"
|
||||
github.com/gohugoio/localescompressed="v1.0.1"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/extras="v0.7.0"
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.5.0"
|
||||
github.com/golang-jwt/jwt/v5="v5.3.0"
|
||||
github.com/google/go-cmp="v0.7.0"
|
||||
github.com/google/s2a-go="v0.1.9"
|
||||
github.com/google/uuid="v1.6.0"
|
||||
github.com/google/wire="v0.7.0"
|
||||
github.com/googleapis/enterprise-certificate-proxy="v0.3.14"
|
||||
github.com/googleapis/gax-go/v2="v2.21.0"
|
||||
github.com/gorilla/css="v1.0.1"
|
||||
github.com/gorilla/websocket="v1.5.3"
|
||||
github.com/hairyhenderson/go-codeowners="v0.7.0"
|
||||
github.com/hashicorp/golang-lru/v2="v2.0.7"
|
||||
github.com/jdkato/prose="v1.2.1"
|
||||
github.com/josharian/intern="v1.0.0"
|
||||
github.com/kr/pretty="v0.3.1"
|
||||
github.com/kr/text="v0.2.0"
|
||||
github.com/kylelemons/godebug="v1.1.0"
|
||||
github.com/kyokomi/emoji/v2="v2.2.13"
|
||||
github.com/mailru/easyjson="v0.7.7"
|
||||
github.com/makeworld-the-better-one/dither/v2="v2.4.0"
|
||||
github.com/marekm4/color-extractor="v1.2.1"
|
||||
github.com/mattn/go-colorable="v0.1.13"
|
||||
github.com/mattn/go-isatty="v0.0.20"
|
||||
github.com/mattn/go-colorable="v0.1.14"
|
||||
github.com/mattn/go-isatty="v0.0.22"
|
||||
github.com/mattn/go-runewidth="v0.0.19"
|
||||
github.com/microcosm-cc/bluemonday="v1.0.27"
|
||||
github.com/mitchellh/mapstructure="v1.5.1-0.20231216201459-8508981c8b6c"
|
||||
github.com/mohae/deepcopy="v0.0.0-20170929034955-c48cc78d4826"
|
||||
github.com/muesli/smartcrop="v0.3.0"
|
||||
github.com/niklasfasching/go-org="v1.9.1"
|
||||
github.com/oasdiff/yaml3="v0.0.0-20250309153720-d2182401db90"
|
||||
github.com/oasdiff/yaml="v0.0.0-20250309154309-f31be36b4037"
|
||||
github.com/oasdiff/yaml3="v0.0.13"
|
||||
github.com/oasdiff/yaml="v0.1.0"
|
||||
github.com/olekukonko/cat="v0.0.0-20250911104152-50322a0618f6"
|
||||
github.com/olekukonko/errors="v1.1.0"
|
||||
github.com/olekukonko/ll="v0.1.3"
|
||||
github.com/olekukonko/tablewriter="v1.1.2"
|
||||
github.com/olekukonko/errors="v1.2.0"
|
||||
github.com/olekukonko/ll="v0.1.6"
|
||||
github.com/olekukonko/tablewriter="v1.1.4"
|
||||
github.com/pbnjay/memory="v0.0.0-20210728143218-7b4eea64cf58"
|
||||
github.com/pelletier/go-toml/v2="v2.2.4"
|
||||
github.com/perimeterx/marshmallow="v1.1.5"
|
||||
github.com/pelletier/go-toml/v2="v2.4.3"
|
||||
github.com/pkg/browser="v0.0.0-20240102092130-5ac0b6a4141c"
|
||||
github.com/pkg/errors="v0.9.1"
|
||||
github.com/rogpeppe/go-internal="v1.14.1"
|
||||
github.com/rogpeppe/go-internal="v1.15.0"
|
||||
github.com/russross/blackfriday/v2="v2.1.0"
|
||||
github.com/sass/dart-sass/compiler="1.97.1"
|
||||
github.com/sass/dart-sass/implementation="1.97.1"
|
||||
github.com/sass/dart-sass/protocol="3.2.0"
|
||||
github.com/santhosh-tekuri/jsonschema/v6="v6.0.2"
|
||||
github.com/spf13/afero="v1.15.0"
|
||||
github.com/spf13/cast="v1.10.0"
|
||||
github.com/spf13/cobra="v1.10.2"
|
||||
github.com/spf13/fsync="v0.10.1"
|
||||
github.com/spf13/pflag="v1.0.9"
|
||||
github.com/tdewolff/minify/v2="v2.24.8"
|
||||
github.com/tdewolff/parse/v2="v2.8.5"
|
||||
github.com/tetratelabs/wazero="v1.10.1"
|
||||
github.com/spf13/pflag="v1.0.10"
|
||||
github.com/spiffe/go-spiffe/v2="v2.6.0"
|
||||
github.com/tdewolff/minify/v2="v2.24.13"
|
||||
github.com/tdewolff/parse/v2="v2.8.12"
|
||||
github.com/tetratelabs/wazero="v1.12.0"
|
||||
github.com/webmproject/libwebp="v1.6.0"
|
||||
github.com/woodsbury/decimal128="v1.3.0"
|
||||
github.com/yuin/goldmark-emoji="v1.0.6"
|
||||
github.com/yuin/goldmark="v1.7.13"
|
||||
github.com/yuin/goldmark="v1.8.2"
|
||||
go.opentelemetry.io/auto/sdk="v1.2.1"
|
||||
go.opentelemetry.io/contrib/detectors/gcp="v1.39.0"
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc="v0.67.0"
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp="v0.67.0"
|
||||
go.opentelemetry.io/otel/metric="v1.43.0"
|
||||
go.opentelemetry.io/otel/sdk/metric="v1.43.0"
|
||||
go.opentelemetry.io/otel/sdk="v1.43.0"
|
||||
go.opentelemetry.io/otel/trace="v1.43.0"
|
||||
go.opentelemetry.io/otel="v1.43.0"
|
||||
go.uber.org/automaxprocs="v1.5.3"
|
||||
go.yaml.in/yaml/v3="v3.0.4"
|
||||
golang.org/x/crypto="v0.46.0"
|
||||
golang.org/x/image="v0.34.0"
|
||||
golang.org/x/mod="v0.31.0"
|
||||
golang.org/x/net="v0.48.0"
|
||||
golang.org/x/sync="v0.19.0"
|
||||
golang.org/x/sys="v0.39.0"
|
||||
golang.org/x/text="v0.32.0"
|
||||
golang.org/x/tools="v0.40.0"
|
||||
google.golang.org/protobuf="v1.36.10"
|
||||
gopkg.in/yaml.v3="v3.0.1"
|
||||
gocloud.dev="v0.45.0"
|
||||
golang.org/x/crypto="v0.53.0"
|
||||
golang.org/x/image="v0.43.0"
|
||||
golang.org/x/mod="v0.37.0"
|
||||
golang.org/x/net="v0.56.0"
|
||||
golang.org/x/oauth2="v0.36.0"
|
||||
golang.org/x/sync="v0.21.0"
|
||||
golang.org/x/sys="v0.46.0"
|
||||
golang.org/x/text="v0.38.0"
|
||||
golang.org/x/time="v0.15.0"
|
||||
golang.org/x/tools="v0.47.0"
|
||||
golang.org/x/xerrors="v0.0.0-20240903120638-7835f813f4da"
|
||||
google.golang.org/api="v0.276.0"
|
||||
google.golang.org/genproto/googleapis/api="v0.0.0-20260319201613-d00831a3d3e7"
|
||||
google.golang.org/genproto/googleapis/rpc="v0.0.0-20260401024825-9d38bb4040a9"
|
||||
google.golang.org/genproto="v0.0.0-20260319201613-d00831a3d3e7"
|
||||
google.golang.org/grpc="v1.80.0"
|
||||
google.golang.org/protobuf="v1.36.11"
|
||||
rsc.io/qr="v0.2.0"
|
||||
software.sslmate.com/src/go-pkcs12="v0.2.0"
|
||||
software.sslmate.com/src/go-pkcs12="v0.7.0"
|
||||
```
|
||||
</details>
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
@@ -59,6 +60,29 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
|
||||
counter := 0
|
||||
|
||||
seen := c.entryLocker.seen
|
||||
seenByLower := make(map[string]string, seen.Len())
|
||||
for id := range seen.All() {
|
||||
seenByLower[strings.ToLower(id)] = id
|
||||
}
|
||||
|
||||
// Names on disk matching a used cache key except for the case, and the used keys
|
||||
// actually walked. See the note about case-insensitive filesystems below.
|
||||
var candidates map[string]string
|
||||
visited := make(map[string]bool, seen.Len())
|
||||
|
||||
remove := func(name string) error {
|
||||
err := c.Fs.Remove(name)
|
||||
if err == nil {
|
||||
counter++
|
||||
return nil
|
||||
}
|
||||
if !herrors.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err := afero.Walk(c.Fs, "", func(name string, info os.FileInfo, err error) error {
|
||||
if info == nil {
|
||||
return nil
|
||||
@@ -93,27 +117,44 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
|
||||
shouldRemove := force || c.isExpired(info.ModTime())
|
||||
|
||||
if !shouldRemove && c.entryLocker.seen.Len() > 0 {
|
||||
// Remove it if it's not been touched/used in the last build.
|
||||
shouldRemove = !c.entryLocker.seen.Has(name)
|
||||
if seen.Has(name) {
|
||||
visited[name] = true
|
||||
} else if !shouldRemove && seen.Len() > 0 {
|
||||
if id, found := seenByLower[strings.ToLower(name)]; found {
|
||||
// On case-insensitive filesystems this is the same file as id; e.g. an
|
||||
// entry created before Hugo started lowercasing the content paths in
|
||||
// v0.123 (content/MyBundle => _gen/images/MyBundle). Decided once the
|
||||
// walk is done: if id is walked too, they are distinct files and this
|
||||
// one is stale. See issue 15101.
|
||||
if candidates == nil {
|
||||
candidates = make(map[string]string)
|
||||
}
|
||||
candidates[name] = id
|
||||
} else {
|
||||
// Remove it if it's not been touched/used in the last build.
|
||||
shouldRemove = true
|
||||
}
|
||||
}
|
||||
|
||||
if shouldRemove {
|
||||
err := c.Fs.Remove(name)
|
||||
if err == nil {
|
||||
counter++
|
||||
}
|
||||
|
||||
if err != nil && !herrors.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return remove(name)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return counter, err
|
||||
}
|
||||
|
||||
return counter, err
|
||||
for name, id := range candidates {
|
||||
if visited[id] {
|
||||
if err := remove(name); err != nil {
|
||||
return counter, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return counter, nil
|
||||
}
|
||||
|
||||
func (c *Cache) pruneRootDirs(force bool) (int, error) {
|
||||
|
||||
@@ -20,11 +20,72 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/cache/filecache"
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
// A cache entry created before Hugo started lowercasing content paths in v0.123
|
||||
// (e.g. _gen/images/MyBundle) is on a case-insensitive filesystem the same file as
|
||||
// the lowercased cache key used today, and must not be pruned.
|
||||
// See issue 15101.
|
||||
func TestPruneCacheEntryWithOtherCase(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
dir := t.TempDir()
|
||||
if isCaseInsensitive, err := htesting.IsCaseInsensitiveFs(dir); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !isCaseInsensitive {
|
||||
t.Skip("skip test on case-sensitive filesystem")
|
||||
}
|
||||
|
||||
fs := afero.NewBasePathFs(afero.NewOsFs(), dir)
|
||||
newCache := func() *filecache.Cache {
|
||||
return filecache.NewCache(fs, filecache.FileCacheConfig{Dir: "cache", MaxAge: -1})
|
||||
}
|
||||
|
||||
c.Assert(newCache().SetBytes("MyBundle/i1", []byte("abc")), qt.IsNil)
|
||||
|
||||
cache := newCache()
|
||||
_, b, err := cache.GetOrCreateBytes("mybundle/i1", func() ([]byte, error) {
|
||||
return []byte("def"), nil
|
||||
})
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(string(b), qt.Equals, "abc")
|
||||
|
||||
count, err := cache.Prune(false)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(count, qt.Equals, 0)
|
||||
c.Assert(cache.GetString("MyBundle/i1"), qt.Equals, "abc")
|
||||
}
|
||||
|
||||
// On a case-sensitive filesystem the entries above are distinct files,
|
||||
// and the one not used in this build should be pruned.
|
||||
func TestPruneCacheEntryWithOtherCaseCaseSensitiveFs(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
cache := filecache.NewCache(fs, filecache.FileCacheConfig{Dir: "cache", MaxAge: -1})
|
||||
|
||||
c.Assert(cache.SetBytes("MyBundle/i1", []byte("abc")), qt.IsNil)
|
||||
|
||||
cache = filecache.NewCache(fs, filecache.FileCacheConfig{Dir: "cache", MaxAge: -1})
|
||||
_, b, err := cache.GetOrCreateBytes("mybundle/i1", func() ([]byte, error) {
|
||||
return []byte("def"), nil
|
||||
})
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(string(b), qt.Equals, "def")
|
||||
|
||||
count, err := cache.Prune(false)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(count, qt.Equals, 1)
|
||||
c.Assert(cache.GetString("MyBundle/i1"), qt.Equals, "")
|
||||
c.Assert(cache.GetString("mybundle/i1"), qt.Equals, "def")
|
||||
}
|
||||
|
||||
func TestPrune(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -38,13 +38,20 @@ run_gofmt() {
|
||||
|
||||
# Run staticcheck
|
||||
run_staticcheck() {
|
||||
# Check if staticcheck is installed, install if not
|
||||
if ! command -v staticcheck &> /dev/null; then
|
||||
local staticcheck_bin
|
||||
if command -v staticcheck &> /dev/null; then
|
||||
staticcheck_bin=$(command -v staticcheck)
|
||||
else
|
||||
echo "==> Installing staticcheck..."
|
||||
go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
staticcheck_bin="$(go env GOBIN)"
|
||||
if [ -z "$staticcheck_bin" ]; then
|
||||
staticcheck_bin="$(go env GOPATH)/bin"
|
||||
fi
|
||||
staticcheck_bin="$staticcheck_bin/staticcheck"
|
||||
fi
|
||||
echo "==> Running staticcheck..."
|
||||
staticcheck $PACKAGES
|
||||
"$staticcheck_bin" $PACKAGES
|
||||
echo " OK"
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,8 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"github.com/bep/simplecobra"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
@@ -34,6 +30,7 @@ import (
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
"github.com/gohugoio/hugo/markup/highlight"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
@@ -47,6 +44,10 @@ func newGenCommand() *genCommand {
|
||||
|
||||
// Chroma flags.
|
||||
style string
|
||||
mode string
|
||||
modeSelector bool
|
||||
classDark string
|
||||
classLight string
|
||||
highlightStyle string
|
||||
lineNumbersInlineStyle string
|
||||
lineNumbersTableStyle string
|
||||
@@ -63,44 +64,41 @@ func newGenCommand() *genCommand {
|
||||
See https://gohugo.io/quick-reference/syntax-highlighting-styles/ for a preview of the available styles.`,
|
||||
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
style = strings.ToLower(style)
|
||||
if !slices.Contains(styles.Names(), style) {
|
||||
return fmt.Errorf("invalid style: %s", style)
|
||||
}
|
||||
builder := styles.Get(style).Builder()
|
||||
if highlightStyle != "" {
|
||||
builder.Add(chroma.LineHighlight, highlightStyle)
|
||||
}
|
||||
if lineNumbersInlineStyle != "" {
|
||||
builder.Add(chroma.LineNumbers, lineNumbersInlineStyle)
|
||||
}
|
||||
if lineNumbersTableStyle != "" {
|
||||
builder.Add(chroma.LineNumbersTable, lineNumbersTableStyle)
|
||||
}
|
||||
style, err := builder.Build()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if omitEmpty {
|
||||
// See https://github.com/alecthomas/chroma/commit/5b2a4c5a26c503c79bc86ba3c4ae5b330028bd3d
|
||||
hugo.Deprecate("--omitEmpty", "Flag is no longer needed, empty classes are now always omitted.", "v0.149.0")
|
||||
}
|
||||
options := []html.Option{
|
||||
html.WithCSSComments(!omitClassComments),
|
||||
html.WithCustomCSS(chromaCSSOverrides(style)),
|
||||
css, err := highlight.ChromaStylesCSS(highlight.ChromaStylesOptions{
|
||||
Style: style,
|
||||
Mode: mode,
|
||||
ModeSelector: modeSelector,
|
||||
ClassDark: classDark,
|
||||
ClassLight: classLight,
|
||||
HighlightStyle: highlightStyle,
|
||||
LineNumbersInlineStyle: lineNumbersInlineStyle,
|
||||
LineNumbersTableStyle: lineNumbersTableStyle,
|
||||
OmitClassComments: omitClassComments,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatter := html.New(options...)
|
||||
|
||||
w := os.Stdout
|
||||
fmt.Fprintf(w, "/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " "))
|
||||
formatter.WriteCSS(w, style)
|
||||
fmt.Printf("/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " "))
|
||||
fmt.Print(css)
|
||||
return nil
|
||||
},
|
||||
withc: func(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.ValidArgsFunction = cobra.NoFileCompletions
|
||||
cmd.PersistentFlags().StringVar(&style, "style", "friendly", "highlighter style")
|
||||
_ = cmd.RegisterFlagCompletionFunc("style", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&mode, "mode", "", `style mode ("light", "dark")`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("mode", cobra.FixedCompletions([]string{"light", "dark"}, cobra.ShellCompDirectiveNoFileComp))
|
||||
cmd.PersistentFlags().BoolVar(&modeSelector, "modeSelector", false, `scope selectors under a top level mode class, e.g. ".dark .chroma"`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("modeSelector", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&classDark, "classDark", "dark", `class name used by --modeSelector for dark styles`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("classDark", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&classLight, "classLight", "light", `class name used by --modeSelector for light styles`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("classLight", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&highlightStyle, "highlightStyle", "", `foreground and background colors for highlighted lines, e.g. --highlightStyle "#fff000 bg:#000fff"`)
|
||||
_ = cmd.RegisterFlagCompletionFunc("highlightStyle", cobra.NoFileCompletions)
|
||||
cmd.PersistentFlags().StringVar(&lineNumbersInlineStyle, "lineNumbersInlineStyle", "", `foreground and background colors for inline line numbers, e.g. --lineNumbersInlineStyle "#fff000 bg:#000fff"`)
|
||||
@@ -276,33 +274,6 @@ url: %s
|
||||
}
|
||||
}
|
||||
|
||||
func chromaCSSOverrides(style *chroma.Style) map[chroma.TokenType]string {
|
||||
bg := style.Get(chroma.Background)
|
||||
m := make(map[chroma.TokenType]string)
|
||||
for tt := range chroma.StandardTypes {
|
||||
if tt == chroma.Background || !style.Has(tt) || !chromaLeafToken(tt) {
|
||||
continue
|
||||
}
|
||||
entry := style.Get(tt)
|
||||
if !entry.Sub(bg).IsZero() || !entry.Colour.IsSet() {
|
||||
continue
|
||||
}
|
||||
if css := html.StyleEntryToCSS(chroma.StyleEntry{Colour: entry.Colour}); css != "" {
|
||||
m[tt] = css
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func chromaLeafToken(tt chroma.TokenType) bool {
|
||||
for other := range chroma.StandardTypes {
|
||||
if other != tt && (other.Category() == tt || other.SubCategory() == tt) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type genCommand struct {
|
||||
rootCmd *rootCommand
|
||||
|
||||
|
||||
@@ -1108,7 +1108,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
|
||||
|
||||
fromPath := ev.Name
|
||||
|
||||
relPath, found := sourceFs.MakePathRelative(fromPath, true)
|
||||
relPath, found := sourceFs.MakePathRelative(fromPath, false)
|
||||
|
||||
if !found {
|
||||
// Not member of this virtual host.
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
@@ -105,6 +106,16 @@ func MD5FromStringHexEncoded(f string) string {
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// MD5FromReaderHexEncoded returns the MD5 hash of the given reader.
|
||||
func MD5FromReaderHexEncoded(r io.Reader) string {
|
||||
h := md5.New()
|
||||
_, err := io.Copy(h, r)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// HashString returns a hash from the given elements.
|
||||
// It will panic if the hash cannot be calculated.
|
||||
// Note that this hash should be used primarily for identity, not for change detection as
|
||||
@@ -124,11 +135,38 @@ func HashStringHex(vs ...any) string {
|
||||
var hashOptsPool = sync.Pool{
|
||||
New: func() any {
|
||||
return &hashstructure.HashOptions{
|
||||
Hasher: xxhash.New(),
|
||||
Hasher: xxhash.New(),
|
||||
UnwrapFunc: unwrapForHashing,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// hashstructure only sees exported struct fields, so rewrite known identity types before hashing,
|
||||
// e.g. a Resource or Page nested in an options map hashes by its Key.
|
||||
func unwrapForHashing(v reflect.Value) (reflect.Value, error) {
|
||||
if v.Kind() != reflect.Struct {
|
||||
return v, nil
|
||||
}
|
||||
var in any
|
||||
if v.CanAddr() {
|
||||
// The common case; pointer receiver methods on a struct
|
||||
// reached through a pointer.
|
||||
in = v.Addr().Interface()
|
||||
} else {
|
||||
in = v.Interface()
|
||||
}
|
||||
switch t := in.(type) {
|
||||
case hashstructure.Hashable:
|
||||
// Let hashstructure handle it.
|
||||
return v, nil
|
||||
case keyer:
|
||||
return reflect.ValueOf(t.Key()), nil
|
||||
case identity.IdentityProvider:
|
||||
return reflect.ValueOf(t.GetIdentity()), nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func getHashOpts() *hashstructure.HashOptions {
|
||||
return hashOptsPool.Get().(*hashstructure.HashOptions)
|
||||
}
|
||||
@@ -145,15 +183,10 @@ func putHashOpts(opts *hashstructure.HashOptions) {
|
||||
func HashUint64(vs ...any) uint64 {
|
||||
var o any
|
||||
if len(vs) == 1 {
|
||||
o = toHashable(vs[0])
|
||||
o = vs[0]
|
||||
} else {
|
||||
elements := make([]any, len(vs))
|
||||
for i, e := range vs {
|
||||
elements[i] = toHashable(e)
|
||||
}
|
||||
o = elements
|
||||
o = vs
|
||||
}
|
||||
|
||||
hash, err := Hash(o)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -176,19 +209,6 @@ type keyer interface {
|
||||
Key() string
|
||||
}
|
||||
|
||||
// For structs, hashstructure.Hash only works on the exported fields,
|
||||
// so rewrite the input slice for known identity types.
|
||||
func toHashable(v any) any {
|
||||
switch t := v.(type) {
|
||||
case keyer:
|
||||
return t.Key()
|
||||
case identity.IdentityProvider:
|
||||
return t.GetIdentity()
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
type xxhashReadFrom struct {
|
||||
buff []byte
|
||||
*xxhash.Digest
|
||||
|
||||
@@ -21,8 +21,9 @@ import (
|
||||
type HasBytesWriter struct {
|
||||
Patterns []*HasBytesPattern
|
||||
|
||||
i int
|
||||
done bool
|
||||
// The tail of the bytes written so far, retained so we can detect a
|
||||
// pattern that straddles the boundary between two Write calls.
|
||||
buff []byte
|
||||
}
|
||||
|
||||
@@ -31,10 +32,13 @@ type HasBytesPattern struct {
|
||||
Pattern []byte
|
||||
}
|
||||
|
||||
func (h *HasBytesWriter) patternLen() int {
|
||||
// maxPatternLen returns the length of the longest pattern.
|
||||
func (h *HasBytesWriter) maxPatternLen() int {
|
||||
l := 0
|
||||
for _, p := range h.Patterns {
|
||||
l += len(p.Pattern)
|
||||
if len(p.Pattern) > l {
|
||||
l = len(p.Pattern)
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
@@ -44,36 +48,55 @@ func (h *HasBytesWriter) Write(p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
if len(h.buff) == 0 {
|
||||
h.buff = make([]byte, h.patternLen()*2)
|
||||
keep := h.maxPatternLen() - 1
|
||||
|
||||
// Join the tail retained from previous Writes with the head of this chunk
|
||||
// so a pattern straddling the boundary is still detected. Only the
|
||||
// boundary window is copied; the chunk itself is scanned in place below.
|
||||
var boundary []byte
|
||||
if keep > 0 && len(h.buff) > 0 {
|
||||
head := p
|
||||
if len(head) > keep {
|
||||
head = head[:keep]
|
||||
}
|
||||
boundary = make([]byte, 0, len(h.buff)+len(head))
|
||||
boundary = append(boundary, h.buff...)
|
||||
boundary = append(boundary, head...)
|
||||
}
|
||||
|
||||
for i := range p {
|
||||
h.buff[h.i] = p[i]
|
||||
h.i++
|
||||
if h.i == len(h.buff) {
|
||||
// Shift left.
|
||||
copy(h.buff, h.buff[len(h.buff)/2:])
|
||||
h.i = len(h.buff) / 2
|
||||
// Scan each not-yet-matched pattern once per Write instead of once per byte.
|
||||
done := true
|
||||
for _, pp := range h.Patterns {
|
||||
if pp.Match {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, pp := range h.Patterns {
|
||||
if bytes.Contains(h.buff, pp.Pattern) {
|
||||
pp.Match = true
|
||||
done := true
|
||||
for _, ppp := range h.Patterns {
|
||||
if !ppp.Match {
|
||||
done = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if done {
|
||||
h.done = true
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
if bytes.Contains(p, pp.Pattern) || bytes.Contains(boundary, pp.Pattern) {
|
||||
pp.Match = true
|
||||
continue
|
||||
}
|
||||
done = false
|
||||
}
|
||||
|
||||
if done {
|
||||
// All patterns found; no need to look at any more data.
|
||||
h.done = true
|
||||
h.buff = nil
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Retain the last keep bytes of (previous tail + this chunk) to detect a
|
||||
// pattern straddling into the next Write.
|
||||
switch {
|
||||
case keep <= 0:
|
||||
h.buff = h.buff[:0]
|
||||
case len(p) >= keep:
|
||||
h.buff = append(h.buff[:0], p[len(p)-keep:]...)
|
||||
default:
|
||||
// Chunk shorter than keep: slide the window over the retained tail.
|
||||
if total := len(h.buff) + len(p); total > keep {
|
||||
h.buff = h.buff[total-keep:]
|
||||
}
|
||||
h.buff = append(h.buff, p...)
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
|
||||
@@ -65,3 +65,61 @@ func TestHasBytesWriter(t *testing.T) {
|
||||
fmt.Fprintf(w, "__foo")
|
||||
c.Assert(h.Patterns[0].Match, qt.Equals, true)
|
||||
}
|
||||
|
||||
func TestHasBytesWriterMultiplePatterns(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
neww := func() (*HasBytesWriter, io.Writer) {
|
||||
var b bytes.Buffer
|
||||
h := &HasBytesWriter{
|
||||
Patterns: []*HasBytesPattern{
|
||||
{Pattern: []byte("__hdeferred/")},
|
||||
{Pattern: []byte("__h_pp_l1")},
|
||||
},
|
||||
}
|
||||
return h, io.MultiWriter(&b, h)
|
||||
}
|
||||
|
||||
// Neither pattern present.
|
||||
h, w := neww()
|
||||
fmt.Fprint(w, "the quick brown fox jumps over the lazy dog")
|
||||
c.Assert(h.Patterns[0].Match, qt.Equals, false)
|
||||
c.Assert(h.Patterns[1].Match, qt.Equals, false)
|
||||
c.Assert(h.done, qt.Equals, false)
|
||||
|
||||
// Only the second pattern present; the writer must not report a match
|
||||
// for the first, and must not prematurely mark itself done.
|
||||
h, w = neww()
|
||||
fmt.Fprint(w, "prefix __h_pp_l1 suffix")
|
||||
c.Assert(h.Patterns[0].Match, qt.Equals, false)
|
||||
c.Assert(h.Patterns[1].Match, qt.Equals, true)
|
||||
c.Assert(h.done, qt.Equals, false)
|
||||
|
||||
// Both patterns present across multiple writes; done once all match.
|
||||
h, w = neww()
|
||||
fmt.Fprint(w, "aaa __hdef")
|
||||
fmt.Fprint(w, "erred/xyz bbb __h_p")
|
||||
fmt.Fprint(w, "p_l1 ccc")
|
||||
c.Assert(h.Patterns[0].Match, qt.Equals, true)
|
||||
c.Assert(h.Patterns[1].Match, qt.Equals, true)
|
||||
c.Assert(h.done, qt.Equals, true)
|
||||
}
|
||||
|
||||
func BenchmarkHasBytesWriter(b *testing.B) {
|
||||
// A large chunk of output containing neither pattern is the common case
|
||||
// (a normal rendered page): the writer must scan all of it.
|
||||
content := []byte(strings.Repeat("<div class=\"nav\"><a href=\"/foo/bar\">baz</a></div>\n", 4000))
|
||||
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
h := &HasBytesWriter{
|
||||
Patterns: []*HasBytesPattern{
|
||||
{Pattern: []byte("__hdeferred/")},
|
||||
{Pattern: []byte("__h_pp_l1")},
|
||||
},
|
||||
}
|
||||
if _, err := h.Write(content); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/version"
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
"github.com/gohugoio/hugo/internal/warpc"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
@@ -211,7 +212,10 @@ func GetDependencyList() []string {
|
||||
|
||||
// GetDependencyListNonGo returns a list of non-Go dependencies.
|
||||
func GetDependencyListNonGo() []string {
|
||||
deps := []string{formatDep("github.com/webmproject/libwebp", "v1.6.0")} // via WASM. TODO(bep) get versions from the plugin setup.
|
||||
var deps []string
|
||||
for _, dep := range warpc.GetWASMDeps() {
|
||||
deps = append(deps, formatDep(dep[0], dep[1]))
|
||||
}
|
||||
|
||||
if IsExtended {
|
||||
deps = append(
|
||||
@@ -225,7 +229,8 @@ func GetDependencyListNonGo() []string {
|
||||
if IsDartSassGeV2() {
|
||||
dartSassPath = "github.com/sass/dart-sass"
|
||||
}
|
||||
deps = append(deps,
|
||||
deps = append(
|
||||
deps,
|
||||
formatDep(dartSassPath+"/protocol", dartSass.ProtocolVersion),
|
||||
formatDep(dartSassPath+"/compiler", dartSass.CompilerVersion),
|
||||
formatDep(dartSassPath+"/implementation", dartSass.ImplementationVersion),
|
||||
|
||||
@@ -35,8 +35,8 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) {
|
||||
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelError)
|
||||
|
||||
// Added just to find the threshold for where we can remove deprecated items.
|
||||
// Subtract 5 from the minor version of the first ERRORed version => 0.136.0.
|
||||
c.Assert(deprecationLogLevelFromVersion("0.141.0"), qt.Equals, logg.LevelError)
|
||||
// Subtract 5 from the minor version of the first ERRORed version => 0.145.0.
|
||||
c.Assert(deprecationLogLevelFromVersion("0.150.0"), qt.Equals, logg.LevelError)
|
||||
}
|
||||
|
||||
func TestMarkupScope(t *testing.T) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import "github.com/gohugoio/hugo/common/version"
|
||||
// This should be the only one.
|
||||
var CurrentVersion = version.Version{
|
||||
Major: 0,
|
||||
Minor: 163,
|
||||
PatchLevel: 3,
|
||||
Minor: 165,
|
||||
PatchLevel: 0,
|
||||
Suffix: "",
|
||||
}
|
||||
|
||||
@@ -458,36 +458,6 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy privacy values.
|
||||
if c.Privacy.Twitter.Disable {
|
||||
hugo.DeprecateWithLogger("project config key privacy.twitter.disable", "Use privacy.x.disable instead.", "v0.141.0", logger.Logger())
|
||||
c.Privacy.X.Disable = c.Privacy.Twitter.Disable
|
||||
}
|
||||
if c.Privacy.Twitter.EnableDNT {
|
||||
hugo.DeprecateWithLogger("project config key privacy.twitter.enableDNT", "Use privacy.x.enableDNT instead.", "v0.141.0", logger.Logger())
|
||||
c.Privacy.X.EnableDNT = c.Privacy.Twitter.EnableDNT
|
||||
}
|
||||
if c.Privacy.Twitter.Simple {
|
||||
hugo.DeprecateWithLogger("project config key privacy.twitter.simple", "Use privacy.x.simple instead.", "v0.141.0", logger.Logger())
|
||||
c.Privacy.X.Simple = c.Privacy.Twitter.Simple
|
||||
}
|
||||
|
||||
// Legacy services values.
|
||||
if c.Services.Twitter.DisableInlineCSS {
|
||||
hugo.DeprecateWithLogger("project config key services.twitter.disableInlineCSS", "Use services.x.disableInlineCSS instead.", "v0.141.0", logger.Logger())
|
||||
c.Services.X.DisableInlineCSS = c.Services.Twitter.DisableInlineCSS
|
||||
}
|
||||
|
||||
// Legacy permalink tokens
|
||||
for _, pc := range c.Permalinks {
|
||||
if strings.Contains(pc.Pattern, ":filename") {
|
||||
hugo.DeprecateWithLogger("the \":filename\" permalink token", "Use \":contentbasename\" instead.", "0.144.0", logger.Logger())
|
||||
}
|
||||
if strings.Contains(pc.Pattern, ":slugorfilename") {
|
||||
hugo.DeprecateWithLogger("the \":slugorfilename\" permalink token", "Use \":slugorcontentbasename\" instead.", "0.144.0", logger.Logger())
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy render hook values.
|
||||
alternativeDetails := fmt.Sprintf(
|
||||
"Set to %q if previous value was false, or set to %q if previous value was true.",
|
||||
|
||||
@@ -25,11 +25,13 @@ weight = 2
|
||||
[[module.mounts]]
|
||||
source = 'content/en'
|
||||
target = 'content'
|
||||
lang = 'en'
|
||||
[module.mounts.sites.matrix]
|
||||
languages = 'en'
|
||||
[[module.mounts]]
|
||||
source = 'content/sv'
|
||||
target = 'content'
|
||||
lang = 'sv'
|
||||
[module.mounts.sites.matrix]
|
||||
languages = 'sv'
|
||||
-- content/en/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
|
||||
@@ -19,19 +19,23 @@ defaultContentLanguage = 'en'
|
||||
[[module.mounts]]
|
||||
source = 'content/en'
|
||||
target = 'content/en'
|
||||
lang = 'en'
|
||||
[module.mounts.sites.matrix]
|
||||
languages = 'en'
|
||||
[[module.mounts]]
|
||||
source = 'content/nn'
|
||||
target = 'content/nn'
|
||||
lang = 'nn'
|
||||
[module.mounts.sites.matrix]
|
||||
languages = 'nn'
|
||||
[[module.mounts]]
|
||||
source = 'content/no'
|
||||
target = 'content/no'
|
||||
lang = 'no'
|
||||
[module.mounts.sites.matrix]
|
||||
languages = 'no'
|
||||
[[module.mounts]]
|
||||
source = 'content/sv'
|
||||
target = 'content/sv'
|
||||
lang = 'sv'
|
||||
[module.mounts.sites.matrix]
|
||||
languages = 'sv'
|
||||
[[module.mounts]]
|
||||
source = 'layouts'
|
||||
target = 'layouts'
|
||||
|
||||
@@ -30,7 +30,6 @@ type Config struct {
|
||||
Disqus Disqus
|
||||
GoogleAnalytics GoogleAnalytics
|
||||
Instagram Instagram
|
||||
Twitter Twitter `json:"-"` // deprecated in favor of X in v0.141.0
|
||||
Vimeo Vimeo
|
||||
YouTube YouTube
|
||||
X X
|
||||
@@ -59,19 +58,6 @@ type Instagram struct {
|
||||
Simple bool
|
||||
}
|
||||
|
||||
// Twitter holds the privacy configuration settings related to the Twitter shortcode.
|
||||
// Deprecated in favor of X in v0.141.0.
|
||||
type Twitter struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
// When set to true, the Tweet and its embedded page on your site are not used
|
||||
// for purposes that include personalized suggestions and personalized ads.
|
||||
EnableDNT bool
|
||||
|
||||
// If simple mode is enabled, a static and no-JS version of the Tweet will be built.
|
||||
Simple bool
|
||||
}
|
||||
|
||||
// Vimeo holds the privacy configuration settings related to the Vimeo shortcode.
|
||||
type Vimeo struct {
|
||||
Service `mapstructure:",squash"`
|
||||
|
||||
@@ -39,12 +39,11 @@ const securityConfigKey = "security"
|
||||
var DefaultConfig = Config{
|
||||
Exec: Exec{
|
||||
Allow: MustNewWhitelist(
|
||||
"^(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded.
|
||||
"^go$", // for Go Modules
|
||||
"^git$", // For Git info
|
||||
"^node$", // Used as the runtime for Node tools.
|
||||
"^(dart-)?sass$", // sass, dart-sass
|
||||
"^go$", // for Go Modules
|
||||
"^git$", // For Git info
|
||||
"^node$", // Used as the runtime for Node tools.
|
||||
"^postcss$",
|
||||
"^tailwindcss$",
|
||||
),
|
||||
// These have been tested to work with Hugo's external programs
|
||||
// on Windows, Linux and MacOS.
|
||||
|
||||
@@ -135,7 +135,7 @@ func TestToTOML(t *testing.T) {
|
||||
got := DefaultConfig.ToTOML()
|
||||
|
||||
c.Assert(got, qt.Equals,
|
||||
"[security]\n allowContent = ['! ^text/html$']\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^git$', '^node$', '^postcss$', '^tailwindcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|PROGRAMDATA)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['(?i)^https?://[a-z0-9]', '! ^https?://\\d+\\.', '! (?i)localhost', '! (?i)^https?://[^/?#]*@']\n\n [security.node]\n [security.node.permissions]\n allowAddons = ['tailwindcss']\n allowChildProcess = ['tailwindcss']\n allowRead = ['.']\n allowWorker = ['tailwindcss']\n allowWrite = []\n disable = false",
|
||||
"[security]\n allowContent = ['! ^text/html$']\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass$', '^go$', '^git$', '^node$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|PROGRAMDATA)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['(?i)^https?://[a-z0-9]', '! ^https?://\\d+\\.', '! (?i)localhost', '! (?i)^https?://[^/?#]*@']\n\n [security.node]\n [security.node.permissions]\n allowAddons = ['tailwindcss']\n allowChildProcess = ['tailwindcss']\n allowRead = ['.']\n allowWorker = ['tailwindcss']\n allowWrite = []\n disable = false",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ type Config struct {
|
||||
Disqus Disqus
|
||||
GoogleAnalytics GoogleAnalytics
|
||||
Instagram Instagram `json:"-"` // the embedded instagram shortcode no longer uses this
|
||||
Twitter Twitter `json:"-"` // deprecated in favor of X in v0.141.0
|
||||
X X
|
||||
RSS RSS
|
||||
}
|
||||
@@ -61,15 +60,6 @@ type Instagram struct {
|
||||
AccessToken string // this is no longer used by the embedded instagram shortcode
|
||||
}
|
||||
|
||||
// Twitter holds the functional configuration settings related to the Twitter shortcodes.
|
||||
// Deprecated in favor of X in v0.141.0.
|
||||
type Twitter struct {
|
||||
// The Simple variant of Twitter is decorated with a basic set of inline styles.
|
||||
// This means that if you want to provide your own CSS, you want
|
||||
// to disable the inline CSS provided by Hugo.
|
||||
DisableInlineCSS bool
|
||||
}
|
||||
|
||||
// X holds the functional configuration settings related to the X shortcodes.
|
||||
type X struct {
|
||||
// The Simple variant of X is decorated with a basic set of inline styles.
|
||||
|
||||
@@ -74,7 +74,6 @@ func TestNewContentFromFile(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
for i, cas := range cases {
|
||||
|
||||
c.Run(cas.name, func(c *qt.C) {
|
||||
c.Parallel()
|
||||
|
||||
@@ -109,7 +108,6 @@ func TestNewContentFromFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +157,37 @@ site RegularPages: {{ len site.RegularPages }}
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "mypage.md")), `draft: true`)
|
||||
}
|
||||
|
||||
// See issue 15078.
|
||||
func TestNewContentWithBuildCascade(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mm := afero.NewMemMapFs()
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(initFs(mm), qt.IsNil)
|
||||
c.Assert(mm.MkdirAll(filepath.Join("content", "posts", "drafts"), 0o755), qt.IsNil)
|
||||
c.Assert(afero.WriteFile(mm, filepath.Join("content", "posts", "drafts", "_index.md"), []byte(`---
|
||||
title: Drafts
|
||||
build:
|
||||
render: never
|
||||
cascade:
|
||||
draft: true
|
||||
build:
|
||||
render: link
|
||||
draft: true
|
||||
---
|
||||
`), 0o755), qt.IsNil)
|
||||
|
||||
cfg, fs := newTestCfg(c, mm)
|
||||
conf := testconfig.GetTestConfigs(fs.Source, cfg)
|
||||
h, err := hugolib.NewHugoSites(deps.DepsCfg{Configs: conf, Fs: fs})
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
const target = "posts/drafts/trip-to-puebla/index.md"
|
||||
c.Assert(create.NewContent(h, "", target, false), qt.IsNil)
|
||||
c.Assert(readFileFromFs(c, fs.Source, filepath.Join("content", target)), qt.Contains, `title: "Trip to Puebla"`)
|
||||
}
|
||||
|
||||
func initFs(fs afero.Fs) error {
|
||||
perm := os.FileMode(0o755)
|
||||
var err error
|
||||
@@ -308,10 +337,10 @@ theme = "mytheme"
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
languageName = "English"
|
||||
label = "English"
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
languageName = "Nynorsk"
|
||||
label = "Nynorsk"
|
||||
|
||||
[module]
|
||||
[[module.mounts]]
|
||||
@@ -320,11 +349,13 @@ languageName = "Nynorsk"
|
||||
[[module.mounts]]
|
||||
source = 'content'
|
||||
target = 'content'
|
||||
lang = 'en'
|
||||
[module.mounts.sites.matrix]
|
||||
languages = 'en'
|
||||
[[module.mounts]]
|
||||
source = 'content_nn'
|
||||
target = 'content'
|
||||
lang = 'nn'
|
||||
[module.mounts.sites.matrix]
|
||||
languages = 'nn'
|
||||
`
|
||||
if mm == nil {
|
||||
mm = afero.NewMemMapFs()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: {{ replace .File.ContentBaseName "-" " " }}
|
||||
params:
|
||||
reference:
|
||||
reference:
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
@@ -8,7 +8,7 @@ _comment: Do not remove front matter.
|
||||
```go-html-template
|
||||
{{ $js := resources.Get "js/main.js" | js.Build (dict "params" (dict "api" "https://example.org/api")) }}
|
||||
```
|
||||
|
||||
|
||||
And then in your JS file:
|
||||
|
||||
```js
|
||||
|
||||
@@ -34,7 +34,7 @@ Time zone offsets|`"Z0700" "Z07:00" "Z07" "Z070000" "Z07:00:00"`
|
||||
{{ $t = time.AsTime $t }}
|
||||
{{ $t = $t.Format "Jan 02, 2006 3:04 PM Z07:00" }}
|
||||
|
||||
{{ $t }} → Jan 27, 2023 11:44 PM -08:00
|
||||
{{ $t }} → Jan 27, 2023 11:44 PM -08:00
|
||||
```
|
||||
|
||||
Strings such as `PST` and `CET` are not time zones. They are time zone _abbreviations_.
|
||||
|
||||
@@ -8,7 +8,7 @@ weight: 40
|
||||
|
||||
## Apache License
|
||||
|
||||
_Version 2.0, January 2004_
|
||||
_Version 2.0, January 2004_
|
||||
_<http://www.apache.org/licenses/>_
|
||||
|
||||
### Terms and Conditions for use, reproduction, and distribution
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Security model
|
||||
linkTitle: Security
|
||||
linkTitle: Security
|
||||
description: A summary of Hugo's security model.
|
||||
categories: []
|
||||
keywords: []
|
||||
|
||||
@@ -112,7 +112,7 @@ The most common front matter fields are `date`, `draft`, `title`, and `weight`,
|
||||
: (`map`) A map to define [sites matrix](g) and [sites complements](g) for the page.
|
||||
|
||||
<!-- markdownlint-disable MD049 -->
|
||||
|
||||
|
||||
{{< code-toggle file=content/_index.md fm=true >}}
|
||||
title = 'Home'
|
||||
[sites.matrix]
|
||||
|
||||
@@ -18,5 +18,5 @@ Try to use the same language (e.g., tagline) everywhere:
|
||||
- Home: https://gohugo.io/
|
||||
- Docs: https://gohugo.io/documentation/
|
||||
- Project repo: https://github.com/gohugoio/hugo?tab=readme-ov-file#readme
|
||||
- Docs repo: https://github.com/gohugoio/hugoDocs?tab=readme-ov-file#readme
|
||||
- Docs repo: https://github.com/gohugoio/hugoDocs?tab=readme-ov-file#readme
|
||||
-->
|
||||
|
||||
@@ -7,7 +7,7 @@ params:
|
||||
functions_and_methods:
|
||||
aliases: [append]
|
||||
returnType: '[]any'
|
||||
signatures:
|
||||
signatures:
|
||||
- collections.Append ELEMENT [ELEMENT...] SLICE
|
||||
- collections.Append SLICE1 SLICE2
|
||||
aliases: [/functions/append]
|
||||
|
||||
@@ -47,7 +47,7 @@ The example above generates the _same_ random numbers each time it is called. To
|
||||
When `N` is greater than `HIGH`, this function returns the full, sorted range [0, `HIGH`) of size `HIGH`:
|
||||
|
||||
```go-html-template
|
||||
{{ collections.D 6 42 7 }} → [0 1 2 3 4 5 6]
|
||||
{{ collections.D 6 42 7 }} → [0 1 2 3 4 5 6]
|
||||
```
|
||||
|
||||
A common use case is the selection of random pages from a page collection. For example, to render a list of 5 random pages using the [day of the year][] as the seed value:
|
||||
|
||||
@@ -12,7 +12,7 @@ aliases: [/functions/shuffle]
|
||||
---
|
||||
|
||||
```go-html-template
|
||||
{{ collections.Shuffle (slice "a" "b" "c") }} → [b a c]
|
||||
{{ collections.Shuffle (slice "a" "b" "c") }} → [b a c]
|
||||
```
|
||||
|
||||
The result will vary from one build to the next.
|
||||
|
||||
@@ -8,7 +8,7 @@ params:
|
||||
aliases: [union]
|
||||
returnType: '[]any'
|
||||
signatures: [collections.Union SLICE1 SLICE2]
|
||||
aliases: [/functions/union]
|
||||
aliases: [/functions/union]
|
||||
---
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: collections.Where
|
||||
title: collections.Where
|
||||
description: Returns a slice by filtering the given slice based on a key, operator, and value.
|
||||
categories: []
|
||||
keywords: []
|
||||
@@ -320,7 +320,7 @@ This template:
|
||||
Is rendered to:
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<ul>
|
||||
<li><a href="/posts/post-2/">Post 2</a></li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: crypto.Hash
|
||||
description: Hashes the given input with the given algorithm and returns its checksum encoded to a hexadecimal string.
|
||||
categories: []
|
||||
keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType: string
|
||||
signatures: ['crypto.Hash [ALGORITHM] INPUT']
|
||||
---
|
||||
|
||||
The `ALGORITHM` is one of `md5`, `sha1`, `sha256` (the default), `sha384`, or `sha512`:
|
||||
|
||||
```go-html-template
|
||||
{{ crypto.Hash "sha256" "Hello world" }} → 64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c
|
||||
{{ "Hello world" | crypto.Hash "sha512" }} → b7f783baed8297f0db917462184ff4f08e69c2d5e5f79a942600f9725f58ce1f29c18139bf80b06c0fff2bdd34738452ecf40c488c22a7e3d80cdf6f9c1c0d47
|
||||
```
|
||||
|
||||
If you omit the algorithm, it defaults to `sha256`:
|
||||
|
||||
```go-html-template
|
||||
{{ "Hello world" | crypto.Hash }} → 64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c
|
||||
```
|
||||
|
||||
The supported algorithms match those used for the [Subresource Integrity] hash in [`.Data.Integrity`] on a fingerprinted resource. Combine `crypto.Hash` with [`encoding.HexDecode`] and [`encoding.Base64Encode`] to construct an SRI hash from a string:
|
||||
|
||||
```go-html-template
|
||||
{{ $algo := "sha256" }}
|
||||
{{ $integrity := printf "%s-%s" $algo ("Hello world" | crypto.Hash $algo | encoding.HexDecode | encoding.Base64Encode) }}
|
||||
```
|
||||
|
||||
[Subresource Integrity]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
|
||||
[`.Data.Integrity`]: /methods/resource/data/
|
||||
[`encoding.HexDecode`]: /functions/encoding/hexdecode/
|
||||
[`encoding.Base64Encode`]: /functions/encoding/base64encode/
|
||||
@@ -175,12 +175,12 @@ The `css.Build` function accepts an options map to fine-tune bundling, minificat
|
||||
`vars`
|
||||
: {{< new-in 0.160.0 />}}
|
||||
: (`map`) A map of key-value pairs used to generate CSS variables. The `css.Build` function injects these variables into the stylesheet when it encounters the `hugo:vars` internal identifier within an `@import` statement.
|
||||
|
||||
|
||||
```go-html-template
|
||||
{{ $vars := dict
|
||||
"font-family" "\"Times New Roman\", Times, serif"
|
||||
"font-size" "24px"
|
||||
"primary-color" "blue"
|
||||
"font-size" "24px"
|
||||
"primary-color" "blue"
|
||||
}}
|
||||
{{ $opts := dict "vars" $vars }}
|
||||
{{ $r := resources.Get "css/main.css" | css.Build $opts }}
|
||||
@@ -226,9 +226,9 @@ The `css.Build` function accepts an options map to fine-tune bundling, minificat
|
||||
"font-family" "\"Times New Roman\", Times, serif"
|
||||
"font-size" "24px"
|
||||
"primary-color" "blue"
|
||||
"mobile" (dict
|
||||
"mobile" (dict
|
||||
"font-size" "12px"
|
||||
"primary-color" "red"
|
||||
"primary-color" "red"
|
||||
)
|
||||
}}
|
||||
{{ $opts := dict "vars" $vars }}
|
||||
|
||||
@@ -33,7 +33,7 @@ The `css.Sass` function accepts an options map.
|
||||
```go-html-template
|
||||
{{ $opts := dict
|
||||
"transpiler" "dartsass"
|
||||
"enableSourceMap" true
|
||||
"enableSourceMap" true
|
||||
}}
|
||||
{{ $r := resources.Get "sass/main.scss" | css.Sass $opts }}
|
||||
```
|
||||
@@ -66,7 +66,7 @@ The `css.Sass` function accepts an options map.
|
||||
```go-html-template
|
||||
{{ $opts := dict
|
||||
"transpiler" "dartsass"
|
||||
"precision" 10
|
||||
"precision" 10
|
||||
}}
|
||||
{{ $r := resources.Get "sass/main.scss" | css.Sass $opts }}
|
||||
```
|
||||
@@ -131,10 +131,10 @@ The `css.Sass` function accepts an options map.
|
||||
```go-html-template
|
||||
{{ $vars := dict
|
||||
"font-family" "\"Times New Roman\", Times, serif"
|
||||
"font-size" "24px"
|
||||
"font-size" "24px"
|
||||
"primary-color" "blue"
|
||||
}}
|
||||
{{ $opts := dict
|
||||
{{ $opts := dict
|
||||
"transpiler" "dartsass"
|
||||
"vars" $vars
|
||||
}}
|
||||
@@ -172,12 +172,12 @@ The `css.Sass` function accepts an options map.
|
||||
"font-family" "\"Times New Roman\", Times, serif"
|
||||
"font-size" "24px"
|
||||
"primary-color" "blue"
|
||||
"mobile" (dict
|
||||
"mobile" (dict
|
||||
"font-size" "12px"
|
||||
"primary-color" "red"
|
||||
)
|
||||
}}
|
||||
{{ $opts := dict
|
||||
{{ $opts := dict
|
||||
"transpiler" "dartsass"
|
||||
"vars" $vars
|
||||
}}
|
||||
@@ -235,7 +235,7 @@ The `css.Sass` function accepts an options map.
|
||||
{{< /code-toggle >}}
|
||||
|
||||
```go-html-template
|
||||
{{ $opts := dict
|
||||
{{ $opts := dict
|
||||
"transpiler" "dartsass"
|
||||
"vars" site.Params.theme.style }}
|
||||
{{ $r := resources.Get "sass/main.scss" | css.Sass $opts }}
|
||||
|
||||
@@ -7,7 +7,7 @@ params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType: debug.Timer
|
||||
signatures: [debug.Timer NAME]
|
||||
signatures: [debug.Timer NAME]
|
||||
---
|
||||
|
||||
Use the `debug.Timer` function to determine execution time for a block of code, useful for finding performance bottlenecks in templates.
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: encoding.HexDecode
|
||||
description: Returns the hexadecimal decoding of the given content.
|
||||
categories: []
|
||||
keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType: string
|
||||
signatures: [encoding.HexDecode INPUT]
|
||||
---
|
||||
|
||||
```go-html-template
|
||||
{{ "48656c6c6f20776f726c64" | encoding.HexDecode }} → Hello world
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: encoding.HexEncode
|
||||
description: Returns the hexadecimal encoding of the given content.
|
||||
categories: []
|
||||
keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType: string
|
||||
signatures: [encoding.HexEncode INPUT]
|
||||
---
|
||||
|
||||
```go-html-template
|
||||
{{ "Hello world" | encoding.HexEncode }} → 48656c6c6f20776f726c64
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Fmt functions
|
||||
linkTitle: fmt
|
||||
linkTitle: fmt
|
||||
description: Use these functions to print strings within a template or to print messages to the terminal.
|
||||
categories: []
|
||||
keywords: []
|
||||
|
||||
@@ -6,7 +6,7 @@ keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType:
|
||||
returnType:
|
||||
signatures: [page]
|
||||
aliases: [/functions/page]
|
||||
---
|
||||
|
||||
@@ -6,7 +6,7 @@ keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType:
|
||||
returnType:
|
||||
signatures: [site]
|
||||
aliases: [/functions/site]
|
||||
---
|
||||
|
||||
@@ -37,7 +37,7 @@ Use with the [`range`][] statement:
|
||||
```go-html-template
|
||||
{{ $var := slice 1 2 3 }}
|
||||
{{ range $var }}
|
||||
{{ . }} → 1 2 3
|
||||
{{ . }} → 1 2 3
|
||||
{{ else }}
|
||||
{{ print "var is falsy" }}
|
||||
{{ end }}
|
||||
|
||||
@@ -33,7 +33,7 @@ Use with the [`range`][] statement:
|
||||
```go-html-template
|
||||
{{ $var := slice 1 2 3 }}
|
||||
{{ range $var }}
|
||||
{{ . }} → 1 2 3
|
||||
{{ . }} → 1 2 3
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType:
|
||||
returnType:
|
||||
signatures: [range COLLECTION]
|
||||
aliases: [/functions/range]
|
||||
---
|
||||
@@ -47,7 +47,7 @@ With this contrived example:
|
||||
```go-html-template
|
||||
{{ $s := slice "foo" "bar" "baz" }}
|
||||
{{ range $s }}
|
||||
{{ .Title }}
|
||||
{{ .Title }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
@@ -67,7 +67,7 @@ This template will render the page title three times:
|
||||
```go-html-template
|
||||
{{ $s := slice "foo" "bar" "baz" }}
|
||||
{{ range $s }}
|
||||
{{ $.Title }}
|
||||
{{ $.Title }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType:
|
||||
returnType:
|
||||
signatures: ['template NAME [CONTEXT]']
|
||||
---
|
||||
|
||||
|
||||
@@ -11,5 +11,5 @@ params:
|
||||
---
|
||||
|
||||
```go-html-template
|
||||
{{ hugo.Generator }} → <meta name="generator" content="Hugo 0.163.2">
|
||||
{{ hugo.Generator }} → <meta name="generator" content="Hugo 0.163.3">
|
||||
```
|
||||
|
||||
@@ -5,7 +5,7 @@ categories: []
|
||||
keywords: []
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
aliases: []
|
||||
returnType: bool
|
||||
signatures: [hugo.IsMultilingual]
|
||||
---
|
||||
|
||||
@@ -11,5 +11,5 @@ params:
|
||||
---
|
||||
|
||||
```go-html-template
|
||||
{{ hugo.Version }} → 0.163.2
|
||||
{{ hugo.Version }} → 0.163.3
|
||||
```
|
||||
|
||||
@@ -56,8 +56,8 @@ Specify `level`, `scale`, and `targetDir` as needed to achieve the desired resul
|
||||
|
||||
```go-html-template
|
||||
{{ $text := "https://gohugo.io" }}
|
||||
{{ $opts := dict
|
||||
"level" "high"
|
||||
{{ $opts := dict
|
||||
"level" "high"
|
||||
"scale" 3
|
||||
"targetDir" "images/qr"
|
||||
}}
|
||||
@@ -94,7 +94,7 @@ Then hide the QR code with CSS unless printing the page:
|
||||
/* Show QR code when printing */
|
||||
@media print {
|
||||
.qr-code {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -45,9 +45,9 @@ The "footer" partial renders the site footer. In this contrived example, the foo
|
||||
You can pass anything in context: a page, a page collection, a scalar value, a slice, or a map. In this example we pass the current page and three scalar values:
|
||||
|
||||
```go-html-template
|
||||
{{ $ctx := dict
|
||||
{{ $ctx := dict
|
||||
"page" .
|
||||
"name" "John Doe"
|
||||
"name" "John Doe"
|
||||
"major" "Finance"
|
||||
"gpa" 4.0
|
||||
}}
|
||||
|
||||
@@ -19,7 +19,7 @@ Let's say you need to publish a file named "site.json" in the root of your `publ
|
||||
```json
|
||||
{
|
||||
"build_date": "2026-04-04T10:46:21-07:00",
|
||||
"hugo_version": "0.163.2",
|
||||
"hugo_version": "0.163.3",
|
||||
"last_modified": "2026-04-04T10:46:26-07:00"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -84,7 +84,7 @@ To post data:
|
||||
{{ $url := "https://example.org/api" }}
|
||||
{{ $opts := dict
|
||||
"method" "post"
|
||||
"body" `{"complete": true}`
|
||||
"body" `{"complete": true}`
|
||||
"headers" (dict "Content-Type" "application/json")
|
||||
}}
|
||||
{{ $resource := resources.GetRemote $url $opts }}
|
||||
@@ -94,7 +94,7 @@ To override the default cache key:
|
||||
|
||||
```go-html-template
|
||||
{{ $url := "https://example.org/images/a.jpg" }}
|
||||
{{ $opts := dict
|
||||
{{ $opts := dict
|
||||
"key" (print $url (now.Format "2006-01-02"))
|
||||
}}
|
||||
{{ $resource := resources.GetRemote $url $opts }}
|
||||
|
||||
@@ -109,9 +109,9 @@ Default is `production` for `hugo build` and `development` for `hugo server`.
|
||||
`HUGO_FILE_X`
|
||||
: Hugo automatically mounts the following files from your project's root directory under `assets/_jsconfig`:
|
||||
|
||||
- `babel.config.js`
|
||||
- `postcss.config.js`
|
||||
- `tailwind.config.js`
|
||||
- `babel.config.js`, `babel.config.mjs`, `babel.config.cjs`
|
||||
- `postcss.config.js`, `postcss.config.mjs`, `postcss.config.cjs`
|
||||
- `tailwind.config.js`, `tailwind.config.mjs`, `tailwind.config.cjs`
|
||||
|
||||
For each file, Hugo creates a corresponding environment variable named `HUGO_FILE_:filename:`, where `:filename:` is the uppercase version of the filename with periods replaced by underscores. This allows you to access these files within your JavaScript, for example:
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: transform.Emojify
|
||||
title: transform.Emojify
|
||||
description: Runs a string through the Emoji emoticons processor.
|
||||
categories: []
|
||||
keywords: []
|
||||
|
||||
@@ -6,7 +6,7 @@ keywords: [highlight]
|
||||
params:
|
||||
functions_and_methods:
|
||||
aliases: []
|
||||
returnType: highlight.HighlightResult
|
||||
returnType: highlight.HighlightResult
|
||||
signatures: ['transform.HighlightCodeBlock CONTEXT [OPTIONS]']
|
||||
---
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ Step 2
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
```
|
||||
|
||||
|
||||
Step 3
|
||||
: In your base template, conditionally include the KaTeX CSS within the head element.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: urls.AbsURL
|
||||
title: urls.AbsURL
|
||||
description: Returns an absolute URL.
|
||||
categories: []
|
||||
keywords: []
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: urls.PathEscape
|
||||
title: urls.PathEscape
|
||||
description: Returns the given string, applying percent-encoding to special characters and reserved delimiters so it can be safely used as a segment within a URL path.
|
||||
categories: []
|
||||
keywords: []
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: urls.PathUnescape
|
||||
title: urls.PathUnescape
|
||||
description: Returns the given string, replacing all percent-encoded sequences with the corresponding unescaped characters.
|
||||
categories: []
|
||||
keywords: []
|
||||
|
||||
@@ -58,7 +58,7 @@ my-project/
|
||||
├── archetypes/
|
||||
│ └── default.md
|
||||
├── assets/
|
||||
├── config/
|
||||
├── config/
|
||||
│ └── _default/
|
||||
│ └── hugo.toml
|
||||
├── content/
|
||||
@@ -115,7 +115,7 @@ Hugo creates a [unified file system](g), allowing you to mount two or more direc
|
||||
```tree
|
||||
home/
|
||||
└── user/
|
||||
├── my-project/
|
||||
├── my-project/
|
||||
│ ├── content/
|
||||
│ │ ├── books/
|
||||
│ │ │ ├── _index.md
|
||||
@@ -125,7 +125,7 @@ home/
|
||||
│ ├── themes/
|
||||
│ │ └── my-theme/
|
||||
│ └── hugo.toml
|
||||
└── shared-content/
|
||||
└── shared-content/
|
||||
└── films/
|
||||
├── _index.md
|
||||
├── film-1.md
|
||||
|
||||
@@ -82,7 +82,7 @@ hugo server
|
||||
The [`hugo server`][] command builds your site and serves your pages using a minimal HTTP server. When you run `hugo server` it will display the URL of your local site:
|
||||
|
||||
```text
|
||||
Web Server is available at http://localhost:1313/
|
||||
Web Server is available at http://localhost:1313/
|
||||
```
|
||||
|
||||
While the server is running, it watches your project directory for changes to assets, configuration, content, data, layouts, translations, and static files. When it detects a change, the server rebuilds your site and refreshes your browser using [LiveReload][].
|
||||
|
||||
@@ -8,11 +8,11 @@ aliases: [/hosting-and-deployment/deployment-with-rclone/]
|
||||
|
||||
## Assumptions
|
||||
|
||||
- A web host running a web server. This could be a shared hosting environment or a VPS.
|
||||
- Access to your web host with any of the [protocols supported by rclone][], such as SFTP.
|
||||
- A web host running a web server. This could be a shared hosting environment or a VPS
|
||||
- Access to your web host with any of the [protocols supported by rclone][], such as SFTP
|
||||
- A functional static website built with Hugo
|
||||
- Deploying from an [Rclone][] compatible operating system
|
||||
- You have [installed Rclone][].
|
||||
- You have [installed Rclone][]
|
||||
|
||||
**NB**: You can remove `--interactive` in the commands below once you are comfortable with rclone, if you wish. Also, `--gc` and `--minify` are optional in the commands below.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ aliases: [/hosting-and-deployment/deployment-with-rsync/]
|
||||
|
||||
## Assumptions
|
||||
|
||||
- A web host running a web server. This could be a shared hosting environment or a VPS.
|
||||
- A web host running a web server. This could be a shared hosting environment or a VPS
|
||||
- Access to your web host with SSH
|
||||
- A functional static website built with Hugo
|
||||
|
||||
@@ -87,7 +87,7 @@ USER=my-user
|
||||
HOST=my-server.com
|
||||
DIR=my/directory/to/topologix.fr/ # the directory where your website files should go
|
||||
|
||||
hugo build && rsync -avz --delete public/ ${USER}@${HOST}:~/${DIR} # this will delete everything on the server that's not in the local public directory
|
||||
hugo build && rsync -avz --delete public/ ${USER}@${HOST}:~/${DIR} # this will delete everything on the server that's not in the local public directory
|
||||
|
||||
exit 0
|
||||
```
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Host on AWS Amplify
|
||||
description: Host your site on AWS Amplify.
|
||||
description: Host your project on AWS Amplify.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/hosting-and-deployment/hosting-on-aws-amplify/]
|
||||
---
|
||||
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply if you are using GitLab for version control.
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply for other Git providers such as GitLab or Bitbucket.
|
||||
|
||||
{{% include "/_common/gitignore-public.md" %}}
|
||||
|
||||
@@ -14,89 +14,114 @@ Use these instructions to enable continuous deployment from a GitHub repository.
|
||||
|
||||
Please complete the following tasks before continuing:
|
||||
|
||||
1. [Create](https://aws.amazon.com/resources/create-account/) an AWS account
|
||||
1. [Log in](https://console.aws.amazon.com/) to your AWS account
|
||||
1. [Create](https://github.com/signup) a GitHub account
|
||||
1. [Log in](https://github.com/login) to your GitHub account
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository
|
||||
1. [Create](https://aws.amazon.com/resources/create-account/) an AWS account.
|
||||
1. [Log in](https://console.aws.amazon.com/) to your AWS account.
|
||||
1. [Create](https://github.com/signup) a GitHub account.
|
||||
1. [Log in](https://github.com/login) to your GitHub account.
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project.
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository.
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command.
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
## Procedure
|
||||
|
||||
This procedure will enable continuous deployment from a GitHub repository. The procedure is essentially the same if you are using GitLab or Bitbucket.
|
||||
|
||||
Step 1
|
||||
: Create a file named `amplify.yml` in the root of your project.
|
||||
|
||||
```sh
|
||||
touch amplify.yml
|
||||
```
|
||||
|
||||
Step 2
|
||||
: Copy and paste the YAML below into the file you created. Change the application versions and time zone as needed.
|
||||
: Create an `amplify.yml` file in the root of your project, adjusting the tool versions and time zone as needed.
|
||||
|
||||
```yaml {file="amplify.yml" copy=true}
|
||||
version: 1
|
||||
env:
|
||||
variables:
|
||||
# Application versions
|
||||
# Define tool versions
|
||||
DART_SASS_VERSION: 1.101.0
|
||||
GO_VERSION: 1.26.4
|
||||
HUGO_VERSION: 0.163.2
|
||||
# Time zone
|
||||
HUGO_VERSION: 0.163.3
|
||||
NODE_VERSION: 24.16.0
|
||||
|
||||
# Set the build time zone
|
||||
TZ: Europe/Oslo
|
||||
# Cache
|
||||
HUGO_CACHEDIR: ${PWD}/.hugo
|
||||
NPM_CONFIG_CACHE: ${PWD}/.npm
|
||||
|
||||
# Set the build cache directory
|
||||
HUGO_CACHEDIR: ${PWD}/.cache/hugo
|
||||
frontend:
|
||||
phases:
|
||||
preBuild:
|
||||
commands:
|
||||
# Create directory for user-specific executable files
|
||||
- echo "Creating directory for user-specific executable files..."
|
||||
# Create a temporary directory for downloads
|
||||
- build_temp_dir=$(mktemp -d)
|
||||
|
||||
# Create a local tools directory
|
||||
- mkdir -p "${HOME}/.local"
|
||||
|
||||
# Install Dart Sass
|
||||
- echo "Installing Dart Sass ${DART_SASS_VERSION}..."
|
||||
- curl -sLJO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
- tar -C "${HOME}/.local" -xf "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
- rm "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
- export PATH="${HOME}/.local/dart-sass:${PATH}"
|
||||
- |
|
||||
echo "Installing Dart Sass ${DART_SASS_VERSION}..."
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/dart-sass:${PATH}"
|
||||
|
||||
# Install Go
|
||||
- echo "Installing Go ${GO_VERSION}..."
|
||||
- curl -sLJO "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
- tar -C "${HOME}/.local" -xf "go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
- rm "go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
- export PATH="${HOME}/.local/go/bin:${PATH}"
|
||||
- |
|
||||
if [[ -f "go.mod" ]]; then
|
||||
echo "Installing Go ${GO_VERSION}..."
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/go/bin:${PATH}"
|
||||
fi
|
||||
|
||||
# Install Hugo
|
||||
- echo "Installing Hugo ${HUGO_VERSION}..."
|
||||
- curl -sLJO "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
- mkdir "${HOME}/.local/hugo"
|
||||
- tar -C "${HOME}/.local/hugo" -xf "hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
- rm "hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
- export PATH="${HOME}/.local/hugo:${PATH}"
|
||||
- |
|
||||
echo "Installing Hugo ${HUGO_VERSION}..."
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
mkdir -p "${HOME}/.local/hugo"
|
||||
tar -C "${HOME}/.local/hugo" -xf "${build_temp_dir}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/hugo:${PATH}"
|
||||
|
||||
# Verify installations
|
||||
- echo "Verifying installations..."
|
||||
- "echo Dart Sass: $(sass --version)"
|
||||
- "echo Go: $(go version)"
|
||||
- "echo Hugo: $(hugo version)"
|
||||
- "echo Node.js: $(node --version)"
|
||||
# Install Node.js
|
||||
- |
|
||||
if [[ -f "package-lock.json" ]]; then
|
||||
echo "Installing Node.js ${NODE_VERSION}..."
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/node-v${NODE_VERSION}-linux-x64/bin:${PATH}"
|
||||
fi
|
||||
|
||||
# Install Node.js dependencies
|
||||
- echo "Installing Node.js dependencies..."
|
||||
- "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci --prefer-offline || true"
|
||||
# Log tool versions
|
||||
- |
|
||||
echo "Logging tool versions..."
|
||||
command -v sass &> /dev/null && echo "Dart Sass: $(sass --version)" || echo "Dart Sass: not installed"
|
||||
command -v go &> /dev/null && echo "Go: $(go version)" || echo "Go: not installed"
|
||||
command -v hugo &> /dev/null && echo "Hugo: $(hugo version)" || echo "Hugo: not installed"
|
||||
command -v node &> /dev/null && echo "Node.js: $(node --version)" || echo "Node.js: not installed"
|
||||
|
||||
# Configure Git
|
||||
- echo "Configuring Git..."
|
||||
- git config --global core.quotepath false
|
||||
- |
|
||||
echo "Configuring Git..."
|
||||
git config --global core.quotepath false
|
||||
|
||||
# Fetch full Git history
|
||||
- |
|
||||
if [[ $(git rev-parse --is-shallow-repository) == true ]]; then
|
||||
echo "Fetching full Git history..."
|
||||
git fetch --unshallow
|
||||
fi
|
||||
|
||||
# Initialize Git submodules
|
||||
- |
|
||||
if [[ -f .gitmodules ]]; then
|
||||
echo "Initializing Git submodules..."
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
|
||||
# Install Node.js dependencies
|
||||
- |
|
||||
if [[ -f package-lock.json ]]; then
|
||||
echo "Installing Node.js dependencies..."
|
||||
npm ci
|
||||
fi
|
||||
build:
|
||||
commands:
|
||||
- echo "Building site..."
|
||||
- echo "Building the project..."
|
||||
- hugo build --gc --minify
|
||||
artifacts:
|
||||
baseDirectory: public
|
||||
@@ -104,10 +129,19 @@ Step 2
|
||||
- '**/*'
|
||||
cache:
|
||||
paths:
|
||||
- ${HUGO_CACHEDIR}/**/*
|
||||
- ${NPM_CONFIG_CACHE}/**/*
|
||||
- .cache/hugo/**/*
|
||||
```
|
||||
|
||||
Step 2
|
||||
: In your project configuration, change the location of the image cache to the [`cacheDir`][] as shown below:
|
||||
|
||||
{{< code-toggle file=hugo copy=true >}}
|
||||
[caches.images]
|
||||
dir = ':cacheDir/images'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
See [configure file caches][] for more information.
|
||||
|
||||
Step 3
|
||||
: Commit and push the change to your GitHub repository.
|
||||
|
||||
@@ -123,27 +157,27 @@ Step 4
|
||||
Step 5
|
||||
: Choose a source code provider, then press the **Next** button.
|
||||
|
||||

|
||||

|
||||
|
||||
Step 6
|
||||
: Authorize AWS Amplify to access your GitHub account.
|
||||
|
||||

|
||||

|
||||
|
||||
Step 7
|
||||
: Select your personal account or relevant organization.
|
||||
|
||||

|
||||

|
||||
|
||||
Step 8
|
||||
: Authorize access to one or more repositories.
|
||||
|
||||

|
||||

|
||||
|
||||
Step 9
|
||||
: Select a repository and branch, then press the **Next** button.
|
||||
|
||||

|
||||

|
||||
|
||||
Step 10
|
||||
: On the "App settings" page, scroll to the bottom then press the **Next** button. Amplify reads the `amplify.yml` file you created in Steps 1-3 instead of using the values on this page.
|
||||
@@ -154,7 +188,9 @@ Step 11
|
||||
Step 12
|
||||
: When your site has finished deploying, press the **Visit deployed URL** button to view your published site.
|
||||
|
||||

|
||||

|
||||
|
||||
[Amplify Console]: https://console.aws.amazon.com/amplify/apps
|
||||
[`cacheDir`]: /configuration/all/#cachedir
|
||||
[configure file caches]: /configuration/caches/
|
||||
[remote]: https://git-scm.com/docs/git-remote
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Host on Azure Static Web Apps
|
||||
description: Host your site on Azure Static Web Apps.
|
||||
description: Host your project on Azure Static Web Apps.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/hosting-and-deployment/hosting-on-azure-static-web-apps/]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
title: Host on Cloudflare
|
||||
description: Host your site on Cloudflare.
|
||||
description: Host your project on Cloudflare.
|
||||
categories: []
|
||||
keywords: []
|
||||
---
|
||||
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply if you are using GitLab for version control.
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply for other Git providers such as GitLab or Bitbucket.
|
||||
|
||||
{{% include "/_common/gitignore-public.md" %}}
|
||||
|
||||
@@ -25,39 +25,53 @@ Please complete the following tasks before continuing:
|
||||
## Procedure
|
||||
|
||||
Step 1
|
||||
: Create a `wrangler.toml` file in the root of your project.
|
||||
: Create a `wrangler.jsonc` file in the root of your project.
|
||||
|
||||
```toml {file="wrangler.toml" copy=true}
|
||||
name = 'hosting-cloudflare-worker'
|
||||
compatibility_date = '2025-07-31'
|
||||
|
||||
[build]
|
||||
command = 'chmod a+x build.sh && ./build.sh'
|
||||
|
||||
[assets]
|
||||
directory = './public'
|
||||
not_found_handling = '404-page'
|
||||
```jsonc {file="wrangler.jsonc" copy=true}
|
||||
{
|
||||
// Set this to the name of your project.
|
||||
"name": "test",
|
||||
// Set this to today's date in YYYY-MM-DD format.
|
||||
"compatibility_date": "2026-06-19",
|
||||
"build": {
|
||||
"command": "chmod a+x build.sh && ./build.sh"
|
||||
},
|
||||
"assets": {
|
||||
"directory": "./public",
|
||||
"not_found_handling": "404-page"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Step 2
|
||||
: Create a `build.sh` file in the root of your project.
|
||||
: Create a `build.sh` file in the root of your project, adjusting the tool versions and time zone as needed.
|
||||
|
||||
```sh {file="build.sh" copy=true}
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# @file
|
||||
# Builds a Hugo site hosted on a Cloudflare Worker.
|
||||
# Builds a Hugo project hosted on a Cloudflare Worker.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Exit on error, undefined variables, or pipe failures
|
||||
set -euo pipefail
|
||||
|
||||
build_temp_dir=""
|
||||
# Define tool versions
|
||||
DART_SASS_VERSION=1.101.0
|
||||
GO_VERSION=1.26.4
|
||||
HUGO_VERSION=0.163.3
|
||||
NODE_VERSION=24.16.0
|
||||
|
||||
# Set the build time zone
|
||||
TZ=Europe/Oslo
|
||||
|
||||
# Set the build cache directory
|
||||
HUGO_CACHEDIR="${PWD}/.cache/hugo"
|
||||
|
||||
# Perform cleanup
|
||||
cleanup() {
|
||||
if [[ -n "${build_temp_dir}" && -d "${build_temp_dir}" ]]; then
|
||||
if [[ -n "${build_temp_dir:-}" && -d "${build_temp_dir}" ]]; then
|
||||
rm -rf "${build_temp_dir}"
|
||||
fi
|
||||
}
|
||||
@@ -66,75 +80,78 @@ Step 2
|
||||
trap cleanup EXIT SIGINT SIGTERM
|
||||
|
||||
main() {
|
||||
# Define tool versions
|
||||
DART_SASS_VERSION=1.101.0
|
||||
GO_VERSION=1.26.4
|
||||
HUGO_VERSION=0.163.2
|
||||
NODE_VERSION=24.16.0
|
||||
# Export the build time zone
|
||||
export TZ
|
||||
|
||||
# Set the build timezone
|
||||
export TZ=Europe/Oslo
|
||||
# Export the build cache directory
|
||||
export HUGO_CACHEDIR
|
||||
|
||||
# Set the build cache directory
|
||||
export HUGO_CACHEDIR="${PWD}/.cache/hugo_cache"
|
||||
|
||||
# Create and move into a temporary directory for downloads
|
||||
# Create a temporary directory for downloads
|
||||
build_temp_dir=$(mktemp -d)
|
||||
pushd "${build_temp_dir}" > /dev/null
|
||||
|
||||
# Create the local tools directory
|
||||
# Create a local tools directory
|
||||
mkdir -p "${HOME}/.local"
|
||||
|
||||
# Install Dart Sass
|
||||
echo "Installing Dart Sass ${DART_SASS_VERSION}..."
|
||||
curl -sLO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/dart-sass:${PATH}"
|
||||
|
||||
# Install Go
|
||||
echo "Installing Go ${GO_VERSION}..."
|
||||
curl -sLO "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/go/bin:${PATH}"
|
||||
if [[ -f "go.mod" ]]; then
|
||||
echo "Installing Go ${GO_VERSION}..."
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/go/bin:${PATH}"
|
||||
fi
|
||||
|
||||
# Install Hugo
|
||||
echo "Installing Hugo ${HUGO_VERSION}..."
|
||||
curl -sLO "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
mkdir -p "${HOME}/.local/hugo"
|
||||
tar -C "${HOME}/.local/hugo" -xf "hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local/hugo" -xf "${build_temp_dir}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/hugo:${PATH}"
|
||||
|
||||
# Install Node.js
|
||||
echo "Installing Node.js ${NODE_VERSION}..."
|
||||
curl -sLO "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz"
|
||||
tar -C "${HOME}/.local" -xf "node-v${NODE_VERSION}-linux-x64.tar.xz"
|
||||
export PATH="${HOME}/.local/node-v${NODE_VERSION}-linux-x64/bin:${PATH}"
|
||||
if [[ -f "package-lock.json" ]]; then
|
||||
echo "Installing Node.js ${NODE_VERSION}..."
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/node-v${NODE_VERSION}-linux-x64/bin:${PATH}"
|
||||
fi
|
||||
|
||||
# Return to the project root
|
||||
popd > /dev/null
|
||||
|
||||
# Verify installations
|
||||
echo "Verifying installations..."
|
||||
echo Dart Sass: "$(sass --version)"
|
||||
echo Go: "$(go version)"
|
||||
echo Hugo: "$(hugo version)"
|
||||
echo Node.js: "$(node --version)"
|
||||
# Log tool versions
|
||||
echo "Logging tool versions..."
|
||||
command -v sass &> /dev/null && echo "Dart Sass: $(sass --version)" || echo "Dart Sass: not installed"
|
||||
command -v go &> /dev/null && echo "Go: $(go version)" || echo "Go: not installed"
|
||||
command -v hugo &> /dev/null && echo "Hugo: $(hugo version)" || echo "Hugo: not installed"
|
||||
command -v node &> /dev/null && echo "Node.js: $(node --version)" || echo "Node.js: not installed"
|
||||
|
||||
# Configure Git
|
||||
echo "Configuring Git..."
|
||||
git config --global core.quotepath false
|
||||
if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then
|
||||
|
||||
# Fetch full Git history
|
||||
if [[ $(git rev-parse --is-shallow-repository) == true ]]; then
|
||||
echo "Fetching full Git history..."
|
||||
git fetch --unshallow
|
||||
fi
|
||||
|
||||
# Initialize Git submodules
|
||||
if [[ -f .gitmodules ]]; then
|
||||
echo "Initializing Git submodules..."
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
|
||||
# Install Node.js dependencies
|
||||
if [ -f package-lock.json ]; then
|
||||
if [[ -f package-lock.json ]]; then
|
||||
echo "Installing Node.js dependencies..."
|
||||
npm ci
|
||||
fi
|
||||
|
||||
# Build the site
|
||||
echo "Building the site..."
|
||||
# Build the project
|
||||
echo "Building the project..."
|
||||
hugo build --gc --minify
|
||||
}
|
||||
|
||||
@@ -142,39 +159,49 @@ Step 2
|
||||
```
|
||||
|
||||
Step 3
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
: In your project configuration, change the location of the image cache to the [`cacheDir`][] as shown below:
|
||||
|
||||
{{< code-toggle file=hugo copy=true >}}
|
||||
[caches.images]
|
||||
dir = ':cacheDir/images'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
See [configure file caches][] for more information.
|
||||
|
||||
Step 4
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
Step 5
|
||||
: In the upper right corner of the Cloudflare [dashboard][], press the **Add** button and select "Workers" from the drop down menu.
|
||||
|
||||

|
||||
|
||||
Step 5
|
||||
Step 6
|
||||
: Verify your account if prompted.
|
||||
|
||||

|
||||
|
||||
Step 6
|
||||
Step 7
|
||||
: On the "Create a Worker" page, under the "Ship something new" heading, press the **Connect GitHub** button.
|
||||
|
||||

|
||||
|
||||
Step 7
|
||||
Step 8
|
||||
: Select the GitHub account where you want to install the Cloudflare Workers and Pages application.
|
||||
|
||||

|
||||
|
||||
Step 8
|
||||
Step 9
|
||||
: Authorize the Cloudflare Workers and Pages application to access all repositories or only select repositories, then press the **Install & Authorize** button.
|
||||
|
||||

|
||||
|
||||
Step 9
|
||||
Step 10
|
||||
: On the "Create a Worker" page, under the "Select a repository" heading, select the repository to deploy, then press the **Next** button.
|
||||
|
||||

|
||||
|
||||
Step 10
|
||||
Step 11
|
||||
: On the "Create a Worker" page, under the "Set up your application" heading, perform the following steps:
|
||||
|
||||
1. Provide a **Project name**.
|
||||
@@ -184,7 +211,7 @@ Step 10
|
||||
1. In the **Variable value** field, enter `true`.
|
||||
1. Press the **Deploy** button.
|
||||
|
||||
Step 11
|
||||
Step 12
|
||||
: Wait for the site to build and deploy, then press the **Visit** button in the upper left corner of your screen.
|
||||
|
||||

|
||||
@@ -193,13 +220,58 @@ In the future, whenever you push a change from your local Git repository, Cloudf
|
||||
|
||||
## Build cache
|
||||
|
||||
The build script shown in [Step 2](#step-2) sets Hugo's [cache directory][] to the path required by Cloudflare's build cache, which is disabled by default. To enable the Cloudflare build cache:
|
||||
The build script shown in [Step 2](#step-2) sets Hugo's [`cacheDir`][] to the path required by Cloudflare's build cache, which is disabled by default. To enable the Cloudflare build cache, you must complete two steps.
|
||||
|
||||
First, your project must have both a `package.json` and `package-lock.json` file in the project root. If you have only a package.json file, run `npm install` to create the corresponding `package-lock.json` file. If your project does not require any Node.js packages, create both files by running `npm init -y && npm install`.
|
||||
|
||||
Second, you must enable the build cache in your project dashboard.
|
||||
|
||||
1. Navigate to Workers & Pages Overview on the [dashboard][].
|
||||
1. Find your Workers project.
|
||||
1. Go to **Settings** > **Build** > **Build cache**.
|
||||
1. Go to **Settings** > **Build** > **Build cache**.
|
||||
1. Press the **Enable** button.
|
||||
|
||||
[cache directory]: /configuration/all/#cache-directory
|
||||
## Scheduled builds
|
||||
|
||||
If your site uses [`resources.GetRemote`][] to fetch external data at build time, that data is embedded in the static HTML when the site is built. Without a scheduled build, the data only refreshes when someone commits code to the repository. To keep content current, you can trigger a rebuild on a schedule by creating a Cloudflare deploy hook and calling it from a GitHub Actions workflow.
|
||||
|
||||
Step 1
|
||||
: In the Cloudflare [dashboard][], go to **Workers & Pages**. Select your project, then navigate to **Settings** > **Builds** > **Deploy Hooks**. Press **Create deploy hook**, provide a name (e.g., `github-cron`), and copy the generated URL.
|
||||
|
||||
Step 2
|
||||
: In your GitHub repository, go to **Settings** > **Secrets and variables** > **Actions**. Press **New repository secret**, name it `CLOUDFLARE_DEPLOY_HOOK`, paste the deploy hook URL as the value, and save.
|
||||
|
||||
Step 3
|
||||
: Create a GitHub Actions workflow file in your repository.
|
||||
|
||||
```yaml {file=".github/workflows/scheduled-cloudflare-deploy.yaml" copy=true}
|
||||
name: github-cron
|
||||
on:
|
||||
schedule:
|
||||
- cron: "42 7 * * *"
|
||||
timezone: Etc/UTC
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger Cloudflare deploy hook
|
||||
run: curl -X POST "${{ secrets.CLOUDFLARE_DEPLOY_HOOK }}"
|
||||
```
|
||||
|
||||
Adjust the [`cron`][] expression to set your desired build schedule. In the example above, the job is scheduled to run every day at 7:42 AM UTC.
|
||||
|
||||
Step 4
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
> [!NOTE]
|
||||
> The schedule event can be delayed during periods of high loads of GitHub Actions workflow runs. High load times include the start of every hour. If the load is sufficiently high enough, some queued jobs may be dropped. To decrease the chance of delay, schedule your workflow to run at a different time of the hour, or use a dedicated third-party scheduling service such as [Google Cloud Scheduler][] or [cron-job.org][].
|
||||
|
||||
[`cacheDir`]: /configuration/all/#cachedir
|
||||
[`cron`]: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule
|
||||
[`resources.GetRemote`]: /functions/resources/getremote/
|
||||
[configure file caches]: /configuration/caches/
|
||||
[cron-job.org]: https://cron-job.org/en/
|
||||
[dashboard]: https://dash.cloudflare.com/
|
||||
[remote]: https://git-scm.com/docs/git-remote
|
||||
[Google Cloud Scheduler]: https://docs.cloud.google.com/scheduler/docs/overview
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Host on Firebase
|
||||
description: Host your site on Firebase.
|
||||
description: Host your project on Firebase.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/hosting-and-deployment/hosting-on-firebase/]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Host on GitHub Pages
|
||||
description: Host your site on GitHub Pages.
|
||||
description: Host your project on GitHub Pages.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/hosting-and-deployment/hosting-on-github/]
|
||||
---
|
||||
|
||||
Use these instructions to enable continuous deployment from a GitHub repository using GitHub Actions.
|
||||
Use these instructions to enable continuous deployment from a GitHub repository to GitHub Pages.
|
||||
|
||||
{{% include "/_common/gitignore-public.md" %}}
|
||||
|
||||
@@ -21,12 +21,12 @@ There are three types of GitHub Pages sites: project, user, and organization. Pr
|
||||
|
||||
Please complete the following tasks before continuing:
|
||||
|
||||
1. [Create](https://github.com/signup) a GitHub account
|
||||
1. [Log in][] to your GitHub account
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository
|
||||
1. [Create](https://github.com/signup) a GitHub account.
|
||||
1. [Log in][] to your GitHub account.
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project.
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository.
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command.
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
## Procedure
|
||||
|
||||
@@ -40,25 +40,7 @@ Step 1
|
||||

|
||||
|
||||
Step 2
|
||||
: In your project configuration, change the location of the image cache to the [`cacheDir`][] as shown below:
|
||||
|
||||
{{< code-toggle file=hugo copy=true >}}
|
||||
[caches.images]
|
||||
dir = ':cacheDir/images'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
See [configure file caches][] for more information.
|
||||
|
||||
Step 3
|
||||
: Create a file named `hugo.yaml` in a directory named `.github/workflows`.
|
||||
|
||||
```sh
|
||||
mkdir -p .github/workflows
|
||||
touch .github/workflows/hugo.yaml
|
||||
```
|
||||
|
||||
Step 4
|
||||
: Copy and paste the YAML below into the file you created.
|
||||
: Create a `hugo.yaml` file in the `.github/workflows` directory, adjusting the tool versions and time zone as needed.
|
||||
|
||||
```yaml {file=".github/workflows/hugo.yaml" copy=true}
|
||||
name: Build and deploy
|
||||
@@ -81,10 +63,13 @@ Step 4
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Define tool versions
|
||||
DART_SASS_VERSION: 1.101.0
|
||||
GO_VERSION: 1.26.4
|
||||
HUGO_VERSION: 0.163.2
|
||||
HUGO_VERSION: 0.163.3
|
||||
NODE_VERSION: 24.16.0
|
||||
|
||||
# Set the build time zone
|
||||
TZ: Europe/Oslo
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -92,97 +77,140 @@ Step 4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
|
||||
- name: Setup Pages
|
||||
id: pages
|
||||
uses: actions/configure-pages@v6
|
||||
|
||||
- name: Create a local tools directory
|
||||
run: |
|
||||
mkdir -p "${HOME}/.local"
|
||||
|
||||
- name: Install Go
|
||||
if: hashFiles('go.mod') != ''
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
- name: Setup Node.js
|
||||
|
||||
- name: Install Node.js
|
||||
if: hashFiles('package-lock.json') != ''
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- name: Setup Pages
|
||||
id: pages
|
||||
uses: actions/configure-pages@v6
|
||||
- name: Create directory for user-specific executable files
|
||||
run: |
|
||||
mkdir -p "${HOME}/.local"
|
||||
|
||||
- name: Install Dart Sass
|
||||
run: |
|
||||
curl -sLJO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
rm "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
echo "Installing Dart Sass ${DART_SASS_VERSION}..."
|
||||
curl -sfL --output-dir "${{ runner.temp }}" -O "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${{ runner.temp }}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
echo "${HOME}/.local/dart-sass" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: Install Hugo
|
||||
run: |
|
||||
curl -sLJO "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
echo "Installing Hugo ${HUGO_VERSION}..."
|
||||
curl -sfL --output-dir "${{ runner.temp }}" -O "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
mkdir "${HOME}/.local/hugo"
|
||||
tar -C "${HOME}/.local/hugo" -xf "hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
rm "hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local/hugo" -xf "${{ runner.temp }}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
echo "${HOME}/.local/hugo" >> "${GITHUB_PATH}"
|
||||
- name: Verify installations
|
||||
|
||||
- name: Log tool versions
|
||||
run: |
|
||||
echo "Dart Sass: $(sass --version)"
|
||||
echo "Go: $(go version)"
|
||||
echo "Hugo: $(hugo version)"
|
||||
echo "Node.js: $(node --version)"
|
||||
- name: Install Node.js dependencies
|
||||
run: |
|
||||
[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true
|
||||
echo "Logging tool versions..."
|
||||
command -v sass &> /dev/null && echo "Dart Sass: $(sass --version)" || echo "Dart Sass: not installed"
|
||||
command -v go &> /dev/null && echo "Go: $(go version)" || echo "Go: not installed"
|
||||
command -v hugo &> /dev/null && echo "Hugo: $(hugo version)" || echo "Hugo: not installed"
|
||||
command -v node &> /dev/null && echo "Node.js: $(node --version)" || echo "Node.js: not installed"
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
echo "Configuring Git..."
|
||||
git config --global core.quotepath false
|
||||
|
||||
- name: Fetch full Git history
|
||||
run: |
|
||||
if [[ $(git rev-parse --is-shallow-repository) == true ]]; then
|
||||
echo "Fetching full Git history..."
|
||||
git fetch --unshallow
|
||||
fi
|
||||
|
||||
- name: Initialize Git submodules
|
||||
run: |
|
||||
if [[ -f .gitmodules ]]; then
|
||||
echo "Initializing Git submodules..."
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: |
|
||||
if [[ -f package-lock.json ]]; then
|
||||
echo "Installing Node.js dependencies..."
|
||||
npm ci
|
||||
fi
|
||||
|
||||
- name: Cache restore
|
||||
id: cache-restore
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ${{ runner.temp }}/hugo_cache
|
||||
key: hugo-${{ github.run_id }}
|
||||
restore-keys:
|
||||
hugo-
|
||||
- name: Build the site
|
||||
restore-keys: hugo-
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
echo "Building the project..."
|
||||
hugo build \
|
||||
--gc \
|
||||
--minify \
|
||||
--baseURL "${{ steps.pages.outputs.base_url }}/" \
|
||||
--cacheDir "${{ runner.temp }}/hugo_cache"
|
||||
|
||||
- name: Cache save
|
||||
id: cache-save
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: ${{ runner.temp }}/hugo_cache
|
||||
key: ${{ steps.cache-restore.outputs.cache-primary-key }}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v5
|
||||
with:
|
||||
include-hidden-files: false
|
||||
path: ./public
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v5
|
||||
```
|
||||
|
||||
Step 5
|
||||
Step 3
|
||||
: In your project configuration, change the location of the image cache to the [`cacheDir`][] as shown below:
|
||||
|
||||
{{< code-toggle file=hugo copy=true >}}
|
||||
[caches.images]
|
||||
dir = ':cacheDir/images'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
See [configure file caches][] for more information.
|
||||
|
||||
Step 4
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
Step 6
|
||||
Step 5
|
||||
: From GitHub's main menu, choose **Actions**. You will see something like this:
|
||||
|
||||

|
||||
|
||||
Step 7
|
||||
Step 6
|
||||
: When GitHub has finished building and deploying your site, the color of the status indicator will change to green.
|
||||
|
||||

|
||||
|
||||
Step 8
|
||||
Step 7
|
||||
: Click on the commit message as shown above. Under the deploy step, you will see a link to your live site.
|
||||
|
||||

|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Host on GitLab Pages
|
||||
description: Host your site on GitLab Pages.
|
||||
description: Host your project on GitLab Pages.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/hosting-and-deployment/hosting-on-gitlab/]
|
||||
---
|
||||
|
||||
Use these instructions to enable continuous deployment from a GitLab repository.
|
||||
Use these instructions to enable continuous deployment from a GitLab repository to GitLab Pages.
|
||||
|
||||
{{% include "/_common/gitignore-public.md" %}}
|
||||
|
||||
@@ -14,12 +14,12 @@ Use these instructions to enable continuous deployment from a GitLab repository.
|
||||
|
||||
Please complete the following tasks before continuing:
|
||||
|
||||
1. [Create](https://gitlab.com/users/sign_up) a GitLab account
|
||||
1. [Log in](https://gitlab.com/users/sign_in) to your GitLab account
|
||||
1. [Create](https://gitlab.com/projects/new) a GitLab repository for your project
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitLab repository
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command
|
||||
1. Commit the changes to your local Git repository and push to your GitLab repository
|
||||
1. [Create](https://gitlab.com/users/sign_up) a GitLab account.
|
||||
1. [Log in](https://gitlab.com/users/sign_in) to your GitLab account.
|
||||
1. [Create](https://gitlab.com/projects/new) a GitLab repository for your project.
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitLab repository.
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command.
|
||||
1. Commit the changes to your local Git repository and push to your GitLab repository.
|
||||
|
||||
## BaseURL
|
||||
|
||||
@@ -28,86 +28,38 @@ The [`baseURL`][] in your project configuration must reflect the full URL of you
|
||||
## Procedure
|
||||
|
||||
Step 1
|
||||
: Create a `.gitlab-ci.yml` file in the root of your project.
|
||||
: Create a `.gitlab-ci.yml` file in the root of your project, adjusting the tool versions and time zone as needed.
|
||||
|
||||
```yaml {file=".gitlab-ci.yml" copy=true}
|
||||
variables:
|
||||
# Application versions
|
||||
# Define tool versions
|
||||
DART_SASS_VERSION: 1.101.0
|
||||
HUGO_VERSION: 0.163.2
|
||||
GO_VERSION: 1.26.4
|
||||
HUGO_VERSION: 0.163.3
|
||||
NODE_VERSION: 24.16.0
|
||||
# Git
|
||||
|
||||
# Set the build timezone
|
||||
TZ: Europe/Oslo
|
||||
|
||||
# Set the build cache directory
|
||||
HUGO_CACHEDIR: ${CI_PROJECT_DIR}/.cache/hugo
|
||||
|
||||
# Set the repository clone and fetch strategy
|
||||
GIT_DEPTH: 0
|
||||
GIT_STRATEGY: clone
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
# Time zone
|
||||
TZ: Europe/Oslo
|
||||
|
||||
cache:
|
||||
key: ${CI_COMMIT_REF_SLUG}
|
||||
fallback_keys:
|
||||
- ${CI_DEFAULT_BRANCH}
|
||||
paths:
|
||||
- .cache/hugo
|
||||
image:
|
||||
name: golang:1.26.4-bookworm
|
||||
|
||||
name: buildpack-deps:bookworm
|
||||
pages:
|
||||
stage: deploy
|
||||
script:
|
||||
- |
|
||||
# Create directory for user-specific executable files
|
||||
echo "Creating directory for user-specific executable files..."
|
||||
mkdir -p "${HOME}/.local"
|
||||
|
||||
# Install utilities
|
||||
echo "Installing utilities..."
|
||||
apt-get update
|
||||
apt-get install -y brotli xz-utils zstd
|
||||
|
||||
# Install Dart Sass
|
||||
echo "Installing Dart Sass ${DART_SASS_VERSION}..."
|
||||
curl -sLJO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
rm "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/dart-sass:${PATH}"
|
||||
|
||||
# Install Hugo
|
||||
echo "Installing Hugo ${HUGO_VERSION}..."
|
||||
curl -sLJO "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
mkdir -p "${HOME}/.local/hugo"
|
||||
tar -C "${HOME}/.local/hugo" -xf "hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
rm "hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/hugo:${PATH}"
|
||||
|
||||
# Install Node.js
|
||||
echo "Installing Node.js ${NODE_VERSION}..."
|
||||
curl -sLJO "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz"
|
||||
tar -C "${HOME}/.local" -xf "node-v${NODE_VERSION}-linux-x64.tar.xz"
|
||||
rm "node-v${NODE_VERSION}-linux-x64.tar.xz"
|
||||
export PATH="${HOME}/.local/node-v${NODE_VERSION}-linux-x64/bin:${PATH}"
|
||||
|
||||
# Verify installations
|
||||
echo "Verifying installations..."
|
||||
echo "Dart Sass: $(sass --version)"
|
||||
echo "Go: $(go version)"
|
||||
echo "Hugo: $(hugo version)"
|
||||
echo "Node.js: $(node --version)"
|
||||
echo "brotli: $(brotli --version)"
|
||||
echo "xz: $(xz --version)"
|
||||
echo "zstd: $(zstd --version)"
|
||||
|
||||
# Install Node.js dependencies
|
||||
echo "Installing Node.js dependencies..."
|
||||
[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci --prefer-offline || true
|
||||
|
||||
# Configure Git
|
||||
echo "Configuring Git..."
|
||||
git config --global core.quotepath false
|
||||
|
||||
# Build site
|
||||
echo "Building site..."
|
||||
hugo --gc --minify --baseURL "${CI_PAGES_URL}"
|
||||
|
||||
# Compress published files
|
||||
echo "Compressing published files..."
|
||||
find public/ -type f -regextype posix-extended -regex '.+\.(css|html|js|json|mjs|svg|txt|xml)$' -print0 > files.txt
|
||||
time xargs --null --max-procs=0 --max-args=1 brotli --quality=10 --force --keep < files.txt
|
||||
time xargs --null --max-procs=0 --max-args=1 gzip -9 --force --keep < files.txt
|
||||
- chmod a+x build.sh && ./build.sh
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
@@ -116,20 +68,135 @@ Step 1
|
||||
```
|
||||
|
||||
Step 2
|
||||
: Commit the changes to your local Git repository and push to your GitLab repository.
|
||||
: Create a `build.sh` file in the root of your project.
|
||||
|
||||
```sh {file="build.sh" copy=true}
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# @file
|
||||
# Builds a Hugo project hosted on GitLab Pages.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Exit on error, undefined variables, or pipe failures
|
||||
set -euo pipefail
|
||||
|
||||
# Perform cleanup
|
||||
cleanup() {
|
||||
if [[ -n "${build_temp_dir:-}" && -d "${build_temp_dir}" ]]; then
|
||||
rm -rf "${build_temp_dir}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Register the cleanup trap
|
||||
trap cleanup EXIT SIGINT SIGTERM
|
||||
|
||||
main() {
|
||||
# Create a temporary directory for downloads
|
||||
build_temp_dir=$(mktemp -d)
|
||||
|
||||
# Create a local tools directory
|
||||
mkdir -p "${HOME}/.local"
|
||||
|
||||
# Install utilities
|
||||
echo "Installing utilities..."
|
||||
apt-get update > /dev/null
|
||||
apt-get install -y brotli > /dev/null
|
||||
|
||||
# Install Dart Sass
|
||||
echo "Installing Dart Sass ${DART_SASS_VERSION}..."
|
||||
curl -sfLO --output-dir "${build_temp_dir}" "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/dart-sass:${PATH}"
|
||||
|
||||
# Install Go
|
||||
if [[ -f "${CI_PROJECT_DIR}/go.mod" ]]; then
|
||||
echo "Installing Go ${GO_VERSION}..."
|
||||
curl -sfLO --output-dir "${build_temp_dir}" "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/go/bin:${PATH}"
|
||||
fi
|
||||
|
||||
# Install Hugo
|
||||
echo "Installing Hugo ${HUGO_VERSION}..."
|
||||
curl -sfLO --output-dir "${build_temp_dir}" "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
mkdir -p "${HOME}/.local/hugo"
|
||||
tar -C "${HOME}/.local/hugo" -xf "${build_temp_dir}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/hugo:${PATH}"
|
||||
|
||||
# Install Node.js
|
||||
if [[ -f "${CI_PROJECT_DIR}/package-lock.json" ]]; then
|
||||
echo "Installing Node.js ${NODE_VERSION}..."
|
||||
curl -sfLO --output-dir "${build_temp_dir}" "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/node-v${NODE_VERSION}-linux-x64/bin:${PATH}"
|
||||
fi
|
||||
|
||||
# Log tool versions
|
||||
echo "Logging tool versions..."
|
||||
command -v sass &> /dev/null && echo "Dart Sass: $(sass --version)" || echo "Dart Sass: not installed"
|
||||
command -v go &> /dev/null && echo "Go: $(go version)" || echo "Go: not installed"
|
||||
command -v hugo &> /dev/null && echo "Hugo: $(hugo version)" || echo "Hugo: not installed"
|
||||
command -v node &> /dev/null && echo "Node.js: $(node --version)" || echo "Node.js: not installed"
|
||||
|
||||
# Configure Git
|
||||
echo "Configuring Git..."
|
||||
git config --global core.quotepath false
|
||||
|
||||
# Fetch full Git history
|
||||
if [[ $(git rev-parse --is-shallow-repository) == true ]]; then
|
||||
echo "Fetching full Git history..."
|
||||
git fetch --unshallow
|
||||
fi
|
||||
|
||||
# Initialize Git submodules
|
||||
if [[ -f .gitmodules ]]; then
|
||||
echo "Initializing Git submodules..."
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
|
||||
# Install Node.js dependencies
|
||||
if [[ -f package-lock.json ]]; then
|
||||
echo "Installing Node.js dependencies..."
|
||||
npm ci
|
||||
fi
|
||||
|
||||
# Build the project
|
||||
echo "Building the project..."
|
||||
hugo build --gc --minify
|
||||
|
||||
# Compress published files
|
||||
echo "Compressing published files..."
|
||||
find public/ -type f -regextype posix-extended -regex '.+\.(cjs|css|html|js|json|mjs|svg|txt|xml)$' -print0 > "${build_temp_dir}/files.txt"
|
||||
xargs --null --max-procs=0 --max-args=1 brotli --quality=10 --force --keep < "${build_temp_dir}/files.txt"
|
||||
xargs --null --max-procs=0 --max-args=1 gzip -9 --force --keep < "${build_temp_dir}/files.txt"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
```
|
||||
|
||||
Step 3
|
||||
: From your GitLab repository, navigate to **Build** > **Pipelines** to follow the CI pipeline building your page.
|
||||
: In your project configuration, change the location of the image cache to the [`cacheDir`][] as shown below:
|
||||
|
||||
{{< code-toggle file=hugo copy=true >}}
|
||||
[caches.images]
|
||||
dir = ':cacheDir/images'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
See [configure file caches][] for more information.
|
||||
|
||||
Step 4
|
||||
: Commit the changes to your local Git repository and push to your GitLab repository.
|
||||
|
||||
Step 5
|
||||
: From your GitLab repository, navigate to **Build** > **Pipelines** to follow the CI pipeline building your page.
|
||||
|
||||
Step 6
|
||||
: When the pipeline has passed, your new website is available at `https://<YourUsername>.gitlab.io/<your-hugo-site>/`.
|
||||
|
||||
In the future, whenever you push a change from your local Git repository, GitLab Pages will rebuild and deploy your site.
|
||||
|
||||
## Other resources
|
||||
|
||||
- [GitLab Pages documentation][]
|
||||
|
||||
[GitLab Pages documentation]: https://docs.gitlab.com/user/project/pages/
|
||||
[`baseURL`]: /configuration/all/#baseurl
|
||||
[`cacheDir`]: /configuration/all/#cachedir
|
||||
[configure file caches]: /configuration/caches/
|
||||
[remote]: https://git-scm.com/docs/git-remote
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Host on Netlify
|
||||
description: Host your site on Netlify.
|
||||
description: Host your project on Netlify.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/hosting-and-deployment/hosting-on-netlify/]
|
||||
---
|
||||
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply if you are using Azure DevOps, Bitbucket, or GitLab for version control.
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply for other Git providers such as GitLab or Bitbucket.
|
||||
|
||||
{{% include "/_common/gitignore-public.md" %}}
|
||||
|
||||
@@ -14,27 +14,24 @@ Use these instructions to enable continuous deployment from a GitHub repository.
|
||||
|
||||
Please complete the following tasks before continuing:
|
||||
|
||||
1. [Create](https://app.netlify.com/signup) a Netlify account
|
||||
1. [Log in](https://app.netlify.com/login) to your Netlify account
|
||||
1. [Create](https://github.com/signup) a GitHub account
|
||||
1. [Log in](https://github.com/login) to your GitHub account
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository
|
||||
1. [Create](https://app.netlify.com/signup) a Netlify account.
|
||||
1. [Log in](https://app.netlify.com/login) to your Netlify account.
|
||||
1. [Create](https://github.com/signup) a GitHub account.
|
||||
1. [Log in](https://github.com/login) to your GitHub account.
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project.
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository.
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command.
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
## Procedure
|
||||
|
||||
<!-- Using "text" as the code block language because "toml" looks terrible. -->
|
||||
|
||||
Step 1
|
||||
: Create a `netlify.toml` file in the root of your project.
|
||||
: Create a `netlify.toml` file in the root of your project, adjusting the tool versions and time zone as needed.
|
||||
|
||||
```toml {file="netlify.toml" copy=true}
|
||||
[build.environment]
|
||||
DART_SASS_VERSION = "1.101.0"
|
||||
GO_VERSION = "1.26.4"
|
||||
HUGO_VERSION = "0.163.2"
|
||||
HUGO_VERSION = "0.163.3"
|
||||
NODE_VERSION = "24.16.0"
|
||||
TZ = "Europe/Oslo"
|
||||
|
||||
@@ -46,20 +43,20 @@ Step 1
|
||||
"""
|
||||
```
|
||||
|
||||
If your site requires Dart Sass to transpile Sass to CSS, set the `DART_SASS_VERSION` and include the Dart Sass installation in the build step.
|
||||
If your project requires Dart Sass to transpile Sass to CSS, set the `DART_SASS_VERSION` and include the Dart Sass installation in the build step.
|
||||
|
||||
```toml {file="netlify.toml" copy=true}
|
||||
[build.environment]
|
||||
DART_SASS_VERSION = "1.101.0"
|
||||
GO_VERSION = "1.26.4"
|
||||
HUGO_VERSION = "0.163.2"
|
||||
HUGO_VERSION = "0.163.3"
|
||||
NODE_VERSION = "24.16.0"
|
||||
TZ = "Europe/Oslo"
|
||||
|
||||
[build]
|
||||
publish = "public"
|
||||
command = """\
|
||||
curl -sLJO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz" && \
|
||||
curl -sfLO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz" && \
|
||||
tar -C "${HOME}/.local" -xf "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz" && \
|
||||
rm "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz" && \
|
||||
export PATH="${HOME}/.local/dart-sass:${PATH}" && \
|
||||
@@ -69,57 +66,69 @@ Step 1
|
||||
```
|
||||
|
||||
Step 2
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
: In your project configuration, change the location of the image cache to the [`cacheDir`][] as shown below:
|
||||
|
||||
{{< code-toggle file=hugo copy=true >}}
|
||||
[caches.images]
|
||||
dir = ':cacheDir/images'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
See [configure file caches][] for more information.
|
||||
|
||||
Step 3
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
Step 4
|
||||
: In the upper right corner of the Netlify dashboard, press the **Add new project** button and select “Import an existing project".
|
||||
|
||||

|
||||
|
||||
Step 4
|
||||
Step 5
|
||||
: Connect to GitHub.
|
||||
|
||||

|
||||
|
||||
Step 5
|
||||
Step 6
|
||||
: Press the "Authorize Netlify" button to allow the Netlify application to access your GitHub account.
|
||||
|
||||

|
||||
|
||||
Step 6
|
||||
Step 7
|
||||
: Press the **Configure Netlify on GitHub** button.
|
||||
|
||||
|
||||

|
||||
|
||||
Step 7
|
||||
Step 8
|
||||
: Select the GitHub account where you want to install the Netlify application.
|
||||
|
||||

|
||||
|
||||
Step 8
|
||||
Step 9
|
||||
: Authorize the Netlify application to access all repositories or only select repositories, then press the Install button.
|
||||
|
||||

|
||||
|
||||
Your browser will be redirected to the Netlify dashboard.
|
||||
|
||||
Step 9
|
||||
Step 10
|
||||
: Click on the name of the repository you wish to import.
|
||||
|
||||

|
||||
|
||||
Step 10
|
||||
Step 11
|
||||
: On the "Review configuration" page, enter a project name, leave the settings at their default values, then press the **Deploy** button.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Step 11
|
||||
Step 12
|
||||
: When the deployment completes, click on the link to your published site.
|
||||
|
||||

|
||||
|
||||
In the future, whenever you push a change from your local Git repository, Netlify will rebuild and deploy your site.
|
||||
|
||||
[`cacheDir`]: /configuration/all/#cachedir
|
||||
[configure file caches]: /configuration/caches/
|
||||
[remote]: https://git-scm.com/docs/git-remote
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Host on Render
|
||||
description: Host your site on Render.
|
||||
description: Host your project on Render.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/hosting-and-deployment/hosting-on-render/]
|
||||
---
|
||||
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply if you are using Bitbucket or GitLab for version control.
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply for other Git providers such as GitLab or Bitbucket.
|
||||
|
||||
{{% include "/_common/gitignore-public.md" %}}
|
||||
|
||||
@@ -14,19 +14,19 @@ Use these instructions to enable continuous deployment from a GitHub repository.
|
||||
|
||||
Please complete the following tasks before continuing:
|
||||
|
||||
1. [Create](https://dashboard.render.com/register) a Render account
|
||||
1. [Log in](https://dashboard.render.com/login) to your Render account
|
||||
1. [Create](https://github.com/signup) a GitHub account
|
||||
1. [Log in](https://github.com/login) to your GitHub account
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository
|
||||
1. [Create](https://dashboard.render.com/register) a Render account.
|
||||
1. [Log in](https://dashboard.render.com/login) to your Render account.
|
||||
1. [Create](https://github.com/signup) a GitHub account.
|
||||
1. [Log in](https://github.com/login) to your GitHub account.
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project.
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository.
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command.
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
## Procedure
|
||||
|
||||
Step 1
|
||||
: Create a [Render Blueprint][] in the root of your project.
|
||||
: Create a `render.yaml` file in the root of your project, adjusting the tool versions and time zone as needed.
|
||||
|
||||
```yaml {file="render.yaml" copy=true}
|
||||
services:
|
||||
@@ -42,7 +42,7 @@ Step 1
|
||||
- key: GO_VERSION
|
||||
value: 1.26.4
|
||||
- key: HUGO_VERSION
|
||||
value: 0.163.2
|
||||
value: 0.163.3
|
||||
- key: NODE_VERSION
|
||||
value: 24.16.0
|
||||
- key: TZ
|
||||
@@ -57,15 +57,16 @@ Step 2
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# @file
|
||||
# Builds a Hugo site hosted on a Render.
|
||||
# Builds a Hugo project hosted on Render.
|
||||
#
|
||||
# Render automatically installs Node.js dependencies.
|
||||
# Render automatically installs Node.js and any Node.js dependencies.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Exit on error, undefined variables, or pipe failures
|
||||
set -euo pipefail
|
||||
|
||||
build_temp_dir=""
|
||||
# Set the build cache directory
|
||||
HUGO_CACHEDIR="${PWD}/.cache/hugo"
|
||||
|
||||
# Perform cleanup
|
||||
cleanup() {
|
||||
@@ -78,112 +79,133 @@ Step 2
|
||||
trap cleanup EXIT SIGINT SIGTERM
|
||||
|
||||
main() {
|
||||
# Create and move into a temporary directory for downloads
|
||||
build_temp_dir=$(mktemp -d)
|
||||
pushd "${build_temp_dir}" > /dev/null
|
||||
# Export the build cache directory
|
||||
export HUGO_CACHEDIR
|
||||
|
||||
# Create the local tools directory
|
||||
# Create a temporary directory for downloads
|
||||
build_temp_dir=$(mktemp -d)
|
||||
|
||||
# Create a local tools directory
|
||||
mkdir -p "${HOME}/.local"
|
||||
|
||||
# Install Dart Sass
|
||||
echo "Installing Dart Sass ${DART_SASS_VERSION}..."
|
||||
curl -sLJO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/dart-sass:${PATH}"
|
||||
|
||||
# Install Go
|
||||
echo "Installing Go ${GO_VERSION}..."
|
||||
curl -sLJO "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/go/bin:${PATH}"
|
||||
if [[ -f "go.mod" ]]; then
|
||||
echo "Installing Go ${GO_VERSION}..."
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/go/bin:${PATH}"
|
||||
fi
|
||||
|
||||
# Install Hugo
|
||||
echo "Installing Hugo ${HUGO_VERSION}..."
|
||||
curl -sLJO "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
mkdir -p "${HOME}/.local/hugo"
|
||||
tar -C "${HOME}/.local/hugo" -xf "hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local/hugo" -xf "${build_temp_dir}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/hugo:${PATH}"
|
||||
|
||||
# Return to the project root
|
||||
popd > /dev/null
|
||||
|
||||
# Verify installations
|
||||
echo "Verifying installations..."
|
||||
echo Dart Sass: "$(sass --version)"
|
||||
echo Go: "$(go version)"
|
||||
echo Hugo: "$(hugo version)"
|
||||
echo Node.js: "$(node --version)"
|
||||
# Log tool versions
|
||||
echo "Logging tool versions..."
|
||||
command -v sass &> /dev/null && echo "Dart Sass: $(sass --version)" || echo "Dart Sass: not installed"
|
||||
command -v go &> /dev/null && echo "Go: $(go version)" || echo "Go: not installed"
|
||||
command -v hugo &> /dev/null && echo "Hugo: $(hugo version)" || echo "Hugo: not installed"
|
||||
command -v node &> /dev/null && echo "Node.js: $(node --version)" || echo "Node.js: not installed"
|
||||
|
||||
# Configure Git
|
||||
echo "Configuring Git..."
|
||||
git config --global core.quotepath false
|
||||
if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then
|
||||
|
||||
# Fetch full Git history
|
||||
if [[ $(git rev-parse --is-shallow-repository) == true ]]; then
|
||||
echo "Fetching full Git history..."
|
||||
git fetch --unshallow
|
||||
fi
|
||||
|
||||
# Build the site
|
||||
echo "Building the site..."
|
||||
hugo build --gc --minify --baseURL "${RENDER_EXTERNAL_URL}"
|
||||
# Initialize Git submodules
|
||||
if [[ -f .gitmodules ]]; then
|
||||
echo "Initializing Git submodules..."
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
|
||||
# Build the project
|
||||
echo "Building the project..."
|
||||
hugo build --gc --minify
|
||||
}
|
||||
|
||||
main "$@"
|
||||
```
|
||||
|
||||
Step 3
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
: In your project configuration, change the location of the image cache to the [`cacheDir`][] as shown below:
|
||||
|
||||
{{< code-toggle file=hugo copy=true >}}
|
||||
[caches.images]
|
||||
dir = ':cacheDir/images'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
See [configure file caches][] for more information.
|
||||
|
||||
Step 4
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
Step 5
|
||||
: On the Render [dashboard][], press the **Add new** button and select "Blueprint" from the drop-down menu.
|
||||
|
||||

|
||||
|
||||
Step 5
|
||||
Step 6
|
||||
: Press the **GitHub** button to connect to your GitHub account.
|
||||
|
||||

|
||||
|
||||
Step 6
|
||||
Step 7
|
||||
: Press the **Authorize Render** button to allow the Render application to access your GitHub account.
|
||||
|
||||

|
||||
|
||||
Step 7
|
||||
Step 8
|
||||
: Select the GitHub account where you want to install the Render application.
|
||||
|
||||

|
||||
|
||||
Step 8
|
||||
Step 9
|
||||
: Authorize the Render application to access all repositories or only select repositories, then press the **Install** button.
|
||||
|
||||

|
||||

|
||||
|
||||
Step 9
|
||||
: On the "Create a new Blueprint Instance in My Workspacee" page, press the **Connect** button to the right of the name of your GitHub repository.
|
||||
Step 10
|
||||
: On the "Create a new Blueprint Instance in My Workspace" page, press the **Connect** button to the right of the name of your GitHub repository.
|
||||
|
||||

|
||||
|
||||
Step 10
|
||||
Step 11
|
||||
: Enter a unique name for your Blueprint, then press the **Deploy Blueprint** button at the bottom of the page.
|
||||
|
||||

|
||||
|
||||
Step 11
|
||||
Step 12
|
||||
: Wait for the site to build and deploy, then click on the "Resources" link on the left side of the page.
|
||||
|
||||

|
||||
|
||||
Step 12
|
||||
Step 13
|
||||
: Click on the link to the static site resource.
|
||||
|
||||

|
||||
|
||||
Step 13
|
||||
Step 14
|
||||
: Click on the link to your published site.
|
||||
|
||||

|
||||
|
||||
In the future, whenever you push a change from your local Git repository, Render will rebuild and deploy your site.
|
||||
|
||||
[Render Blueprint]: https://render.com/docs/blueprint-spec
|
||||
[`cacheDir`]: /configuration/all/#cachedir
|
||||
[configure file caches]: /configuration/caches/
|
||||
[dashboard]: https://dashboard.render.com/
|
||||
[remote]: https://git-scm.com/docs/git-remote
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Host on SourceHut Pages
|
||||
description: Host your site on SourceHut Pages.
|
||||
description: Host your project on SourceHut Pages.
|
||||
categories: []
|
||||
keywords: []
|
||||
aliases: [/hosting-and-deployment/hosting-on-sourcehut/]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
title: Host on Vercel
|
||||
description: Host your site on Vercel.
|
||||
description: Host your project on Vercel.
|
||||
categories: []
|
||||
keywords: []
|
||||
---
|
||||
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply if you are using Bitbucket or GitLab for version control.
|
||||
Use these instructions to enable continuous deployment from a GitHub repository. The same general steps apply for other Git providers such as GitLab or Bitbucket.
|
||||
|
||||
{{% include "/_common/gitignore-public.md" %}}
|
||||
|
||||
@@ -13,14 +13,14 @@ Use these instructions to enable continuous deployment from a GitHub repository.
|
||||
|
||||
Please complete the following tasks before continuing:
|
||||
|
||||
1. [Create](https://vercel.com/signup) a Vercel account
|
||||
1. [Log in](https://vercel.com/login) to your Vercel account
|
||||
1. [Create](https://github.com/signup) a GitHub account
|
||||
1. [Log in](https://github.com/login) to your GitHub account
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository
|
||||
1. [Create](https://vercel.com/signup) a Vercel account.
|
||||
1. [Log in](https://vercel.com/login) to your Vercel account.
|
||||
1. [Create](https://github.com/signup) a GitHub account.
|
||||
1. [Log in](https://github.com/login) to your GitHub account.
|
||||
1. [Create](https://github.com/new) a GitHub repository for your project.
|
||||
1. [Create](https://git-scm.com/docs/git-init) a local Git repository for your project with a [remote][] reference to your GitHub repository.
|
||||
1. Create a Hugo project within your local Git repository and test it with the `hugo server` command.
|
||||
1. Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
## Procedure
|
||||
|
||||
@@ -30,28 +30,37 @@ Step 1
|
||||
```json {file="vercel.json" copy=true}
|
||||
{
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"installCommand": "",
|
||||
"buildCommand": "chmod a+x build.sh && ./build.sh",
|
||||
"outputDirectory": "public"
|
||||
}
|
||||
```
|
||||
|
||||
Step 2
|
||||
: Create a `build.sh` file in the root of your project.
|
||||
: Create a `build.sh` file in the root of your project, adjusting the tool versions and time zone as needed.
|
||||
|
||||
```sh {file="build.sh" copy=true}
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# @file
|
||||
# Builds a Hugo site hosted on Vercel.
|
||||
#
|
||||
# The Vercel build image automatically installs Node.js dependencies.
|
||||
# Builds a Hugo project hosted on Vercel.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Exit on error, undefined variables, or pipe failures
|
||||
set -euo pipefail
|
||||
|
||||
build_temp_dir=""
|
||||
# Define tool versions
|
||||
DART_SASS_VERSION=1.101.0
|
||||
GO_VERSION=1.26.4
|
||||
HUGO_VERSION=0.163.3
|
||||
NODE_VERSION=24.16.0
|
||||
|
||||
# Set the build time zone
|
||||
TZ=Europe/Oslo
|
||||
|
||||
# Set the build cache directory
|
||||
HUGO_CACHEDIR="${PWD}/.vercel/cache/hugo"
|
||||
|
||||
# Perform cleanup
|
||||
cleanup() {
|
||||
@@ -64,127 +73,151 @@ Step 2
|
||||
trap cleanup EXIT SIGINT SIGTERM
|
||||
|
||||
main() {
|
||||
# Define tool versions
|
||||
DART_SASS_VERSION=1.101.0
|
||||
GO_VERSION=1.26.4
|
||||
HUGO_VERSION=0.163.2
|
||||
NODE_VERSION=24.16.0
|
||||
# Export the build time zone
|
||||
export TZ
|
||||
|
||||
# Set the build timezone
|
||||
export TZ=Europe/Oslo
|
||||
# Export the build cache directory
|
||||
export HUGO_CACHEDIR
|
||||
|
||||
# Create and move into a temporary directory for downloads
|
||||
# Create a temporary directory for downloads
|
||||
build_temp_dir=$(mktemp -d)
|
||||
pushd "${build_temp_dir}" > /dev/null
|
||||
|
||||
# Create the local tools directory
|
||||
# Create a local tools directory
|
||||
mkdir -p "${HOME}/.local"
|
||||
|
||||
# Install Dart Sass
|
||||
echo "Installing Dart Sass ${DART_SASS_VERSION}..."
|
||||
curl -sLJO "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/dart-sass:${PATH}"
|
||||
|
||||
# Install Go
|
||||
echo "Installing Go ${GO_VERSION}..."
|
||||
curl -sLJO "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/go/bin:${PATH}"
|
||||
if [[ -f "go.mod" ]]; then
|
||||
echo "Installing Go ${GO_VERSION}..."
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/go${GO_VERSION}.linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/go/bin:${PATH}"
|
||||
fi
|
||||
|
||||
# Install Hugo
|
||||
echo "Installing Hugo ${HUGO_VERSION}..."
|
||||
curl -sLJO "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
mkdir -p "${HOME}/.local/hugo"
|
||||
tar -C "${HOME}/.local/hugo" -xf "hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
tar -C "${HOME}/.local/hugo" -xf "${build_temp_dir}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
|
||||
export PATH="${HOME}/.local/hugo:${PATH}"
|
||||
|
||||
# Install Node.js
|
||||
echo "Installing Node.js ${NODE_VERSION}..."
|
||||
curl -sLJO "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz"
|
||||
tar -C "${HOME}/.local" -xf "node-v${NODE_VERSION}-linux-x64.tar.xz"
|
||||
export PATH="${HOME}/.local/node-v${NODE_VERSION}-linux-x64/bin:${PATH}"
|
||||
if [[ -f "package-lock.json" ]]; then
|
||||
echo "Installing Node.js ${NODE_VERSION}..."
|
||||
curl -sfL --output-dir "${build_temp_dir}" -O "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
tar -C "${HOME}/.local" -xf "${build_temp_dir}/node-v${NODE_VERSION}-linux-x64.tar.gz"
|
||||
export PATH="${HOME}/.local/node-v${NODE_VERSION}-linux-x64/bin:${PATH}"
|
||||
fi
|
||||
|
||||
# Return to the project root
|
||||
popd > /dev/null
|
||||
|
||||
# Verify installations
|
||||
echo "Verifying installations..."
|
||||
echo Dart Sass: "$(sass --version)"
|
||||
echo Go: "$(go version)"
|
||||
echo Hugo: "$(hugo version)"
|
||||
echo Node.js: "$(node --version)"
|
||||
# Log tool versions
|
||||
echo "Logging tool versions..."
|
||||
command -v sass &> /dev/null && echo "Dart Sass: $(sass --version)" || echo "Dart Sass: not installed"
|
||||
command -v go &> /dev/null && echo "Go: $(go version)" || echo "Go: not installed"
|
||||
command -v hugo &> /dev/null && echo "Hugo: $(hugo version)" || echo "Hugo: not installed"
|
||||
command -v node &> /dev/null && echo "Node.js: $(node --version)" || echo "Node.js: not installed"
|
||||
|
||||
# Configure Git
|
||||
echo "Configuring Git..."
|
||||
git config --global core.quotepath false
|
||||
if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then
|
||||
|
||||
# Fetch full Git history
|
||||
if [[ $(git rev-parse --is-shallow-repository) == true ]]; then
|
||||
echo "Fetching full Git history..."
|
||||
git fetch --unshallow
|
||||
fi
|
||||
|
||||
# Build the site
|
||||
echo "Building the site..."
|
||||
hugo build --gc --minify --baseURL "https://${VERCEL_PROJECT_PRODUCTION_URL}"
|
||||
# Initialize Git submodules
|
||||
if [[ -f .gitmodules ]]; then
|
||||
echo "Initializing Git submodules..."
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
|
||||
# Install Node.js dependencies
|
||||
if [[ -f package-lock.json ]]; then
|
||||
echo "Installing Node.js dependencies..."
|
||||
npm ci
|
||||
fi
|
||||
|
||||
# Build the project
|
||||
echo "Building the project..."
|
||||
hugo build --gc --minify
|
||||
}
|
||||
|
||||
main "$@"
|
||||
```
|
||||
|
||||
Step 3
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
: In your project configuration, change the location of the image cache to the [`cacheDir`][] as shown below:
|
||||
|
||||
{{< code-toggle file=hugo copy=true >}}
|
||||
[caches.images]
|
||||
dir = ':cacheDir/images'
|
||||
{{< /code-toggle >}}
|
||||
|
||||
See [configure file caches][] for more information.
|
||||
|
||||
Step 4
|
||||
: Commit the changes to your local Git repository and push to your GitHub repository.
|
||||
|
||||
Step 5
|
||||
: In the upper right corner of the Vercel dashboard, press the **Add New** button and select "Project" from the drop down menu.
|
||||
|
||||

|
||||
|
||||
Step 5
|
||||
Step 6
|
||||
: Press the "Continue with GitHub" button.
|
||||
|
||||

|
||||
|
||||
Step 6
|
||||
Step 7
|
||||
: Press the **Authorize Vercel** button to allow the Vercel application to access your GitHub account.
|
||||
|
||||

|
||||
|
||||
Step 7
|
||||
Step 8
|
||||
: Press the **Install** button to install the Vercel application.
|
||||
|
||||

|
||||
|
||||
Step 8
|
||||
Step 9
|
||||
: Select the GitHub account where you want to install the Vercel application.
|
||||
|
||||

|
||||
|
||||
Step 9
|
||||
Step 10
|
||||
: Authorize the Vercel application to access all repositories or only select repositories, then press the **Install** button.
|
||||
|
||||

|
||||
|
||||
Your browser will be redirected to the Cloudflare dashboard.
|
||||
|
||||
Step 10
|
||||
Step 11
|
||||
: Press the **Import** button to the right of the name of your GitHub repository.
|
||||
|
||||

|
||||
|
||||
Step 11
|
||||
Step 12
|
||||
: On the "New Project" page, leave the settings at their default values and press the **Deploy** button.
|
||||
|
||||

|
||||
|
||||
Step 12
|
||||
Step 13
|
||||
: When the deployment completes, press the **Continue to Dashboard" button at the bottom of the page.
|
||||
|
||||

|
||||
|
||||
Step 13
|
||||
Step 14
|
||||
: On the "Production Deployment" page, click on the link to your published site.
|
||||
|
||||

|
||||
|
||||
In the future, whenever you push a change from your local Git repository, Vercel will rebuild and deploy your site.
|
||||
|
||||
[`cacheDir`]: /configuration/all/#cachedir
|
||||
[configure file caches]: /configuration/caches/
|
||||
[remote]: https://git-scm.com/docs/git-remote
|
||||
|
||||
@@ -13,7 +13,7 @@ Modules that need Node packages (e.g. for Tailwind CSS) can declare those depend
|
||||
Each module declares its Node dependencies in a `package.json` file in its root directory, using the standard `dependencies` and `devDependencies` fields.
|
||||
|
||||
<!-- TODO
|
||||
In the admonition below, remove the reference to v0.159.0 somewhere
|
||||
In the admonition below, remove the reference to v0.159.0 somewhere
|
||||
after v0.174.0, 15 minor releases after the improvement.
|
||||
-->
|
||||
|
||||
@@ -37,7 +37,7 @@ project/
|
||||
```
|
||||
|
||||
<!-- TODO
|
||||
In the admonition below, remove the reference to v0.159.0 somewhere
|
||||
In the admonition below, remove the reference to v0.159.0 somewhere
|
||||
after v0.174.0, 15 minor releases after the improvement.
|
||||
-->
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ content/
|
||||
│ │ ├── auction-1.md
|
||||
│ │ └── auction-2.md <-- current section: 2023-11
|
||||
│ ├── 2023-12/
|
||||
│ │ ├── _index.md
|
||||
│ │ ├── _index.md
|
||||
│ │ ├── auction-3.md
|
||||
│ │ └── auction-4.md
|
||||
│ ├── _index.md <-- current section: auctions
|
||||
@@ -35,7 +35,7 @@ content/
|
||||
│ ├── book-1.md
|
||||
│ └── book-2.md <-- current section: books
|
||||
├── films/
|
||||
│ ├── _index.md <-- current section: films
|
||||
│ ├── _index.md <-- current section: films
|
||||
│ ├── film-1.md
|
||||
│ └── film-2.md <-- current section: films
|
||||
└── _index.md <-- current section: home
|
||||
|
||||
@@ -58,7 +58,7 @@ Use these methods on the `Data` object within a _taxonomy_ template.
|
||||
: (`page.Taxonomy`) Returns the `Taxonomy` object, consisting of a map of terms and the [weighted pages](g) associated with each term.
|
||||
|
||||
```go-html-template
|
||||
{{ $taxonomyObject := .Data.Terms }}
|
||||
{{ $taxonomyObject := .Data.Terms }}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
|
||||
@@ -24,7 +24,7 @@ content/
|
||||
│ │ ├── auction-1.md
|
||||
│ │ └── auction-2.md <-- first section: auctions
|
||||
│ ├── 2023-12/
|
||||
│ │ ├── _index.md
|
||||
│ │ ├── _index.md
|
||||
│ │ ├── auction-3.md
|
||||
│ │ └── auction-4.md
|
||||
│ ├── _index.md <-- first section: auctions
|
||||
|
||||
@@ -132,7 +132,7 @@ Use these methods on the `GitInfo` object.
|
||||
|
||||
```go-html-template
|
||||
{{ with .GitInfo }}
|
||||
{{ range .Ancestors | first 5 }}
|
||||
{{ range .Ancestors | first 5 }}
|
||||
{{ .CommitDate.Format "2006-01-02" }}: {{ .Subject }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -142,7 +142,7 @@ Use these methods on the `GitInfo` object.
|
||||
|
||||
```go-html-template
|
||||
{{ with .GitInfo }}
|
||||
{{ range .Ancestors.Reverse | first 5 }}
|
||||
{{ range .Ancestors.Reverse | first 5 }}
|
||||
{{ .CommitDate.Format "2006-01-02" }}: {{ .Subject }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -21,7 +21,7 @@ content/
|
||||
│ ├── book-2.md <-- kind = page IsBranch = false
|
||||
│ └── _index.md <-- kind = section IsBranch = true
|
||||
├── tags
|
||||
│ ├── fiction
|
||||
│ ├── fiction
|
||||
│ │ └── _index.md <-- kind = term IsBranch = true
|
||||
│ └── _index.md <-- kind = taxonomy IsBranch = true
|
||||
└── _index.md <-- kind = home IsBranch = true
|
||||
|
||||
@@ -24,7 +24,7 @@ content/
|
||||
│ │ ├── auction-1.md
|
||||
│ │ └── auction-2.md <-- parent: 2023-11
|
||||
│ ├── 2023-12/
|
||||
│ │ ├── _index.md
|
||||
│ │ ├── _index.md
|
||||
│ │ ├── auction-3.md
|
||||
│ │ └── auction-4.md
|
||||
│ ├── _index.md <-- parent: home
|
||||
@@ -35,7 +35,7 @@ content/
|
||||
│ ├── book-1.md
|
||||
│ └── book-2.md <-- parent: books
|
||||
├── films/
|
||||
│ ├── _index.md <-- parent: home
|
||||
│ ├── _index.md <-- parent: home
|
||||
│ ├── film-1.md
|
||||
│ └── film-2.md <-- parent: films
|
||||
└── _index.md <-- parent: nil
|
||||
|
||||
@@ -98,7 +98,7 @@ A file tree:
|
||||
content/
|
||||
└── s1/
|
||||
├── p1/
|
||||
│ └── index.md
|
||||
│ └── index.md
|
||||
└── p2.md
|
||||
```
|
||||
|
||||
@@ -108,7 +108,7 @@ The same content represented as a logical tree:
|
||||
content/
|
||||
└── s1/
|
||||
├── p1
|
||||
└── p2
|
||||
└── p2
|
||||
```
|
||||
|
||||
A key difference between these trees is the relative path from p1 to p2:
|
||||
|
||||
@@ -25,7 +25,7 @@ content/
|
||||
│ └── book-2.md
|
||||
├── films/
|
||||
│ ├── _index.md
|
||||
│ ├── film-1.md
|
||||
│ ├── film-1.md
|
||||
│ └── film-2.md
|
||||
└── _index.md
|
||||
```
|
||||
|
||||
@@ -84,7 +84,7 @@ Consider this content structure:
|
||||
|
||||
```tree
|
||||
content/
|
||||
├── headless/
|
||||
├── headless/
|
||||
│ ├── a.jpg
|
||||
│ ├── b.jpg
|
||||
│ ├── c.jpg
|
||||
|
||||
@@ -32,7 +32,7 @@ baseURL = 'https://example.org/docs/'
|
||||
Template:
|
||||
|
||||
```go-html-template
|
||||
{{ .Site.Home.Permalink }} → https://example.org/docs/
|
||||
{{ .Site.Home.Permalink }} → https://example.org/docs/
|
||||
{{ .Site.Home.RelPermalink }} → /docs/
|
||||
```
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ Some of the methods above are commonly used in a base template as attributes for
|
||||
|
||||
```go-html-template
|
||||
<html
|
||||
lang="{{ .Site.Language.Locale }}"
|
||||
lang="{{ .Site.Language.Locale }}"
|
||||
dir="{{ or .Site.Language.Direction `ltr` }}"
|
||||
>
|
||||
```
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
title: regular expression
|
||||
reference:
|
||||
---
|
||||
|
||||
A _regular expression_, also known as a _regex_, is a sequence of characters that defines a search pattern. Use the [RE2 syntax][] when defining regular expressions in your templates or in your project configuration.
|
||||
|
||||
@@ -28,8 +28,8 @@ Hugo renders this HTML:
|
||||
```html
|
||||
<figure class="ma0 w-75">
|
||||
<a href="https://www.nps.gov/zion/index.htm">
|
||||
<img
|
||||
src="/images/examples/zion-national-park.jpg"
|
||||
<img
|
||||
src="/images/examples/zion-national-park.jpg"
|
||||
alt="A photograph of Zion National Park"
|
||||
>
|
||||
</a>
|
||||
|
||||
@@ -113,9 +113,9 @@ In the example above:
|
||||
Hugo renders the above to:
|
||||
|
||||
```html {trim=false}
|
||||
|
||||
|
||||
<h2>my page title</h2>
|
||||
|
||||
|
||||
```
|
||||
|
||||
### Whitespace
|
||||
@@ -194,7 +194,7 @@ You can split a template action over two or more lines. For example, these are e
|
||||
```go-html-template
|
||||
{{ $v := or $arg1 $arg2 }}
|
||||
|
||||
{{ $v := or
|
||||
{{ $v := or
|
||||
$arg1
|
||||
$arg2
|
||||
}}
|
||||
@@ -225,8 +225,8 @@ Other than using the `nil` keyword in comparisons, you may not use it as an argu
|
||||
These, on the other hand, are invalid:
|
||||
|
||||
```go-html-template
|
||||
{{ $a := nil }}
|
||||
{{ add 3 nil }}
|
||||
{{ $a := nil }}
|
||||
{{ add 3 nil }}
|
||||
{{ nil | print}}
|
||||
```
|
||||
|
||||
|
||||