mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-25 15:58:53 +00:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b665f1e8f1 | |||
| 6be6752c8a | |||
| 8e9dce1092 | |||
| 00b71668b2 | |||
| beebf2afb0 | |||
| 3f0b40f674 | |||
| 0bd79d30c1 | |||
| 8e77bcc930 | |||
| 74daca6b30 | |||
| 1fd4c562af | |||
| a5cda5ca4d | |||
| 5e2b28d6e6 | |||
| f2019f0a23 | |||
| 475638fe02 | |||
| 203cc54578 | |||
| af23cdca9c | |||
| 90ad804505 | |||
| ab5ce59894 | |||
| 7d40da876c | |||
| 02c89a446d | |||
| 06c3ac6741 | |||
| 5e03de0ddb | |||
| bef31b58aa | |||
| b76146b129 | |||
| 8e5044d7f5 | |||
| 5046a6c7ca | |||
| 160a067c81 | |||
| e0ba1a805a | |||
| 7b49c56a6a | |||
| dffca57883 | |||
| 79932e7225 | |||
| 9eb9b70a29 | |||
| 0ff4a9326f | |||
| 45e1084ff2 | |||
| c98348416c | |||
| 2de393c791 | |||
| 7efb35680f | |||
| ddbcc67122 | |||
| fd75f129b2 | |||
| 14878ca0a0 | |||
| e88873b803 | |||
| 4219993b0c | |||
| 988e1417a0 | |||
| 42529882ce | |||
| 45f1b1cfc5 | |||
| 369bdf22f8 | |||
| d1b03a093f | |||
| 941c28ab0c | |||
| ffbdcc75ab | |||
| 95d9764518 | |||
| b66f9f2665 | |||
| f7e00c039f | |||
| f5ba6fd45c | |||
| c4bbc1eeeb | |||
| 0e0fb1b648 | |||
| b017f7cb01 | |||
| 7fb28085ac | |||
| 0cd1929b9c | |||
| cbdaff2135 | |||
| 3fefea06b8 | |||
| 5c48ba9343 | |||
| 21562e3aad | |||
| 9c24b86e4b |
+96
-33
@@ -1,51 +1,114 @@
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: bepsays/ci-goreleaser:1.1800.300
|
||||
environment:
|
||||
CGO_ENABLED: "0"
|
||||
parameters:
|
||||
|
||||
# v2: 11m.
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.21900.20003
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
<<: *defaults
|
||||
prepare_release:
|
||||
<<: *defaults
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
steps:
|
||||
- checkout:
|
||||
- &remote-docker
|
||||
setup_remote_docker:
|
||||
version: 20.10.14
|
||||
- checkout:
|
||||
path: hugo
|
||||
- &git-config
|
||||
run:
|
||||
command: |
|
||||
git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com"
|
||||
git config --global user.name "hugoreleaser"
|
||||
- run:
|
||||
command: |
|
||||
git clone git@github.com:gohugoio/hugoDocs.git
|
||||
cd hugo
|
||||
go mod download
|
||||
sleep 5
|
||||
go mod verify
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths: .
|
||||
release:
|
||||
<<: *defaults
|
||||
command: |
|
||||
cd hugo
|
||||
go mod download
|
||||
go run -tags release main.go release --step 1
|
||||
- save_cache:
|
||||
key: git-sha-{{ .Revision }}
|
||||
paths:
|
||||
- hugo
|
||||
- gomodcache
|
||||
build_container1:
|
||||
<<: [*defaults]
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /root/project
|
||||
- &restore-cache
|
||||
restore_cache:
|
||||
key: git-sha-{{ .Revision }}
|
||||
- run:
|
||||
command: |
|
||||
cd hugo
|
||||
git config --global user.email "bjorn.erik.pedersen+hugoreleaser@gmail.com"
|
||||
git config --global user.name "hugoreleaser"
|
||||
go run -tags release main.go release -r ${CIRCLE_BRANCH}
|
||||
|
||||
no_output_timeout: 20m
|
||||
command: |
|
||||
mkdir -p /tmp/files/dist1
|
||||
cd hugo
|
||||
hugoreleaser build -paths "builds/container1/**" -workers 3 -dist /tmp/files/dist1 -chunks $CIRCLE_NODE_TOTAL -chunk-index $CIRCLE_NODE_INDEX
|
||||
- &persist-workspace
|
||||
persist_to_workspace:
|
||||
root: /tmp/files
|
||||
paths:
|
||||
- dist1
|
||||
- dist2
|
||||
parallelism: 7
|
||||
build_container2:
|
||||
<<: [*defaults]
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.21900.20003
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: |
|
||||
mkdir -p /tmp/files/dist2
|
||||
cd hugo
|
||||
hugoreleaser build -paths "builds/container2/**" -workers 1 -dist /tmp/files/dist2
|
||||
- *persist-workspace
|
||||
archive_and_release:
|
||||
<<: [*defaults]
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
steps:
|
||||
- *restore-cache
|
||||
- *attach-workspace
|
||||
- *git-config
|
||||
- run:
|
||||
command: |
|
||||
cp -a /tmp/workspace/dist1/. ./hugo/dist
|
||||
cp -a /tmp/workspace/dist2/. ./hugo/dist
|
||||
- run:
|
||||
command: |
|
||||
cd hugo
|
||||
hugoreleaser archive
|
||||
hugoreleaser release
|
||||
go run -tags release main.go release --step 2
|
||||
workflows:
|
||||
version: 2
|
||||
release:
|
||||
jobs:
|
||||
- build:
|
||||
- prepare_release:
|
||||
filters:
|
||||
branches:
|
||||
only: /release-.*/
|
||||
- hold:
|
||||
type: approval
|
||||
- build_container1:
|
||||
requires:
|
||||
- build
|
||||
- release:
|
||||
- prepare_release
|
||||
- build_container2:
|
||||
requires:
|
||||
- prepare_release
|
||||
- archive_and_release:
|
||||
context: org-global
|
||||
requires:
|
||||
- hold
|
||||
- build_container1
|
||||
- build_container2
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,13 @@ on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
stale:
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@08e671be8ac8944d0e132aa71d0ae8ccfb347675
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
# Go 1.18 had some breaking changes on the source level which means Hugo cannot be built
|
||||
# with older Go versions, but the improvements in Go 1.18 were too good to pass on (e.g. break and continue).
|
||||
# Note that you don't need Go (or Go 1.18) to run a pre-built binary.
|
||||
go-version: [1.18.x]
|
||||
go-version: [1.18.x,1.19.x]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@@ -49,9 +49,11 @@ jobs:
|
||||
brew install pandoc
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
choco install pandoc
|
||||
choco install mingw --version 10.2.0 --allow-downgrade
|
||||
Choco-Install -PackageName pandoc
|
||||
- run: pandoc -v
|
||||
- if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
Choco-Install -PackageName mingw -ArgumentList "--version","10.2.0","--allow-downgrade"
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Install dart-sass-embedded Linux
|
||||
run: |
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Copyright 2022 The Hugo Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<a href="https://gohugo.io/"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/hugo-logo-wide.svg?sanitize=true" alt="Hugo" width="565"></a>
|
||||
|
||||
A Fast and Flexible Static Site Generator built with love by [bep](https://github.com/bep), [spf13](https://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go][].
|
||||
A Fast and Flexible Static Site Generator built with love by [bep](https://github.com/bep), [spf13](https://spf13.com/) and [friends](https://github.com/gohugoio/hugo/graphs/contributors) in [Go](https://go.dev/).
|
||||
|
||||
[Website](https://gohugo.io) |
|
||||
[Forum](https://discourse.gohugo.io) |
|
||||
@@ -13,9 +13,19 @@ A Fast and Flexible Static Site Generator built with love by [bep](https://githu
|
||||
[](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
|
||||
[](https://goreportcard.com/report/github.com/gohugoio/hugo)
|
||||
|
||||
* [Overview](#overview)
|
||||
* [Banner Sponsors](#banner-sponsors)
|
||||
* [Supported Architectures](#supported-architectures)
|
||||
* [Choose How to Install](#choose-how-to-install)
|
||||
* [Install Hugo as Your Site Generator (Binary Install)](#install-hugo-as-your-site-generator-binary-install)
|
||||
* [Build and Install the Binaries from Source (Advanced Install)](#build-and-install-the-binaries-from-source-advanced-install)
|
||||
* [The Hugo Documentation](#the-hugo-documentation)
|
||||
* [Contributing to Hugo](#contributing-code-to-hugo)
|
||||
* [Dependencies](#dependencies)
|
||||
|
||||
## Overview
|
||||
|
||||
Hugo is a static HTML and CSS website generator written in [Go][].
|
||||
Hugo is a static HTML and CSS website generator written in [Go](https://go.dev/).
|
||||
It is optimized for speed, ease of use, and configurability.
|
||||
Hugo takes a directory with content and templates and renders them into a full HTML website.
|
||||
|
||||
@@ -27,7 +37,16 @@ A good rule of thumb is that each piece of content renders in around 1 milliseco
|
||||
|
||||
Hugo is designed to work well for any kind of website including blogs, tumbles, and docs.
|
||||
|
||||
#### Supported Architectures
|
||||
## Banner Sponsors
|
||||
<p> </p>
|
||||
<p float="left">
|
||||
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
|
||||
<a href="https://esolia.com/post/why-did-esolia-choose-hugo/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/sponsors/esolia-logo.svg?sanitize=true" width="200" alt="eSOLIA"></a>
|
||||
<a href="https://buttercms.com/hugo-cms/?utm_campaign=sponsorship&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/static/images/sponsors/butter-dark.svg?sanitize=true" width="280" alt="ButterCMS"></a>
|
||||
</p>
|
||||
<p> </p>
|
||||
|
||||
## Supported Architectures
|
||||
|
||||
Currently, we provide pre-built Hugo binaries for Windows, Linux, FreeBSD, NetBSD, DragonFly BSD, OpenBSD, macOS (Darwin), and [Android](https://gist.github.com/bep/a0d8a26cf6b4f8bc992729b8e50b480b) for x64, i386 and ARM architectures.
|
||||
|
||||
@@ -38,7 +57,6 @@ Hugo may also be compiled from source wherever the Go compiler tool chain can ru
|
||||
## Choose How to Install
|
||||
|
||||
If you want to use Hugo as your site generator, simply install the Hugo binaries.
|
||||
The Hugo binaries have no external dependencies.
|
||||
|
||||
To contribute to the Hugo source code or documentation, you should [fork the Hugo GitHub project](https://github.com/gohugoio/hugo#fork-destination-box) and clone it to your local machine.
|
||||
|
||||
@@ -83,7 +101,7 @@ The Hugo documentation now lives in its own repository, see https://github.com/g
|
||||
```bash
|
||||
git clone git@github.com:gohugoio/hugo.git
|
||||
```
|
||||
## Contributing to Hugo
|
||||
## Contributing code to Hugo
|
||||
|
||||
**Note March 16th 2022:** We are currently very constrained on human resources to do code reviews, so we currently require any new Pull Requests to be limited to bug fixes closing an existing issue. Also, we have updated to Go 1.18, but we will currently not accept any generic rewrites, "interface{} to any" replacements and similar.
|
||||
|
||||
@@ -96,34 +114,18 @@ helping to manage issues, etc.
|
||||
|
||||
The Hugo community and maintainers are [very active](https://github.com/gohugoio/hugo/pulse/monthly) and helpful, and the project benefits greatly from this activity.
|
||||
|
||||
### Asking Support Questions
|
||||
## Asking Support Questions
|
||||
|
||||
We have an active [discussion forum](https://discourse.gohugo.io) where users and developers can ask questions.
|
||||
Please don't use the GitHub issue tracker to ask questions.
|
||||
|
||||
### Reporting Issues
|
||||
## Reporting Issues
|
||||
|
||||
If you believe you have found a defect in Hugo or its documentation, use
|
||||
the GitHub issue tracker to report the problem to the Hugo maintainers.
|
||||
If you're not sure if it's a bug or not, start by asking in the [discussion forum](https://discourse.gohugo.io).
|
||||
When reporting the issue, please provide the version of Hugo in use (`hugo version`).
|
||||
|
||||
### Submitting Patches
|
||||
|
||||
The Hugo project welcomes all contributors and contributions regardless of skill or experience level.
|
||||
If you are interested in helping with the project, we will help you with your contribution.
|
||||
Hugo is a very active project with many contributions happening daily.
|
||||
|
||||
We want to create the best possible product for our users and the best contribution experience for our developers,
|
||||
we have a set of guidelines which ensure that all contributions are acceptable.
|
||||
The guidelines are not intended as a filter or barrier to participation.
|
||||
If you are unfamiliar with the contribution process, the Hugo team will help you and teach you how to bring your contribution in accordance with the guidelines.
|
||||
|
||||
For a complete guide to contributing code to Hugo, see the [Contribution Guide](CONTRIBUTING.md).
|
||||
|
||||
[Go]: https://golang.org/
|
||||
[Hugo Documentation]: https://gohugo.io/overview/introduction/
|
||||
|
||||
## Dependencies
|
||||
|
||||
Hugo stands on the shoulder of many great open source libraries.
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestExecute(t *testing.T) {
|
||||
c.Assert(resp.Err, qt.IsNil)
|
||||
result := resp.Result
|
||||
c.Assert(len(result.Sites) == 1, qt.Equals, true)
|
||||
c.Assert(len(result.Sites[0].RegularPages()) == 1, qt.Equals, true)
|
||||
c.Assert(len(result.Sites[0].RegularPages()) == 2, qt.Equals, true)
|
||||
c.Assert(result.Sites[0].Info.Params()["myparam"], qt.Equals, "paramproduction")
|
||||
})
|
||||
|
||||
@@ -362,11 +362,25 @@ weight: 1
|
||||
|
||||
Content
|
||||
|
||||
`)
|
||||
|
||||
writeFile(t, filepath.Join(dir, contentDir, "hügö.md"), `
|
||||
---
|
||||
weight: 2
|
||||
---
|
||||
|
||||
This is hügö.
|
||||
|
||||
`)
|
||||
|
||||
writeFile(t, filepath.Join(dir, "layouts", "_default", "single.html"), `
|
||||
|
||||
Single: {{ .Title }}
|
||||
Single: {{ .Title }}|{{ .Content }}
|
||||
|
||||
`)
|
||||
|
||||
writeFile(t, filepath.Join(dir, "layouts", "404.html"), `
|
||||
404: {{ .Title }}|Not Found.
|
||||
|
||||
`)
|
||||
|
||||
|
||||
+8
-5
@@ -511,12 +511,15 @@ func (c *commandeer) build() error {
|
||||
c.hugo().PrintProcessingStats(os.Stdout)
|
||||
fmt.Println()
|
||||
|
||||
if createCounter, ok := c.publishDirFs.(hugofs.DuplicatesReporter); ok {
|
||||
dupes := createCounter.ReportDuplicates()
|
||||
if dupes != "" {
|
||||
c.logger.Warnln("Duplicate target paths:", dupes)
|
||||
hugofs.WalkFilesystems(c.publishDirFs, func(fs afero.Fs) bool {
|
||||
if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
|
||||
dupes := dfs.ReportDuplicates()
|
||||
if dupes != "" {
|
||||
c.logger.Warnln("Duplicate target paths:", dupes)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
unusedTemplates := c.hugo().Tmpl().(tpl.UnusedTemplatesProvider).UnusedTemplates()
|
||||
for _, unusedTemplate := range unusedTemplates {
|
||||
|
||||
+3
-1
@@ -32,6 +32,7 @@ var _ cmder = (*newCmd)(nil)
|
||||
type newCmd struct {
|
||||
contentEditor string
|
||||
contentType string
|
||||
force bool
|
||||
|
||||
*baseBuilderCmd
|
||||
}
|
||||
@@ -54,6 +55,7 @@ Ensure you run this within the root directory of your site.`,
|
||||
|
||||
cmd.Flags().StringVarP(&cc.contentType, "kind", "k", "", "content type to create")
|
||||
cmd.Flags().StringVar(&cc.contentEditor, "editor", "", "edit new content with this editor, if provided")
|
||||
cmd.Flags().BoolVarP(&cc.force, "force", "f", false, "overwrite file if it already exists")
|
||||
|
||||
cmd.AddCommand(b.newNewSiteCmd().getCommand())
|
||||
cmd.AddCommand(b.newNewThemeCmd().getCommand())
|
||||
@@ -80,7 +82,7 @@ func (n *newCmd) newContent(cmd *cobra.Command, args []string) error {
|
||||
return newUserError("path needs to be provided")
|
||||
}
|
||||
|
||||
return create.NewContent(c.hugo(), n.contentType, args[0])
|
||||
return create.NewContent(c.hugo(), n.contentType, args[0], n.force)
|
||||
}
|
||||
|
||||
func mkdir(x ...string) {
|
||||
|
||||
+11
-12
@@ -17,8 +17,6 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/releaser"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -29,10 +27,9 @@ var _ cmder = (*releaseCommandeer)(nil)
|
||||
type releaseCommandeer struct {
|
||||
cmd *cobra.Command
|
||||
|
||||
version string
|
||||
|
||||
skipPublish bool
|
||||
try bool
|
||||
step int
|
||||
skipPush bool
|
||||
try bool
|
||||
}
|
||||
|
||||
func createReleaser() cmder {
|
||||
@@ -50,9 +47,9 @@ func createReleaser() cmder {
|
||||
return r.release()
|
||||
}
|
||||
|
||||
r.cmd.PersistentFlags().StringVarP(&r.version, "rel", "r", "", "new release version, i.e. 0.25.1")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.skipPublish, "skip-publish", "", false, "skip all publishing pipes of the release")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "simulate a release, i.e. no changes")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.skipPush, "skip-push", "", false, "skip pushing to remote")
|
||||
r.cmd.PersistentFlags().BoolVarP(&r.try, "try", "", false, "no changes")
|
||||
r.cmd.PersistentFlags().IntVarP(&r.step, "step", "", 0, "step to run (1: set new version 2: prepare next dev version)")
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -65,8 +62,10 @@ func (c *releaseCommandeer) flagsToConfig(cfg config.Provider) {
|
||||
}
|
||||
|
||||
func (r *releaseCommandeer) release() error {
|
||||
if r.version == "" {
|
||||
return errors.New("must set the --rel flag to the relevant version number")
|
||||
rel, err := releaser.New(r.skipPush, r.try, r.step)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return releaser.New(r.version, r.skipPublish, r.try).Run()
|
||||
|
||||
return rel.Run()
|
||||
}
|
||||
|
||||
+25
-5
@@ -400,24 +400,31 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
|
||||
}
|
||||
|
||||
// Ignore any query params for the operations below.
|
||||
requestURI := strings.TrimSuffix(r.RequestURI, "?"+r.URL.RawQuery)
|
||||
requestURI, _ := url.PathUnescape(strings.TrimSuffix(r.RequestURI, "?"+r.URL.RawQuery))
|
||||
|
||||
for _, header := range f.c.serverConfig.MatchHeaders(requestURI) {
|
||||
w.Header().Set(header.Key, header.Value)
|
||||
}
|
||||
|
||||
if redirect := f.c.serverConfig.MatchRedirect(requestURI); !redirect.IsZero() {
|
||||
// fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
|
||||
doRedirect := true
|
||||
// This matches Netlify's behaviour and is needed for SPA behaviour.
|
||||
// See https://docs.netlify.com/routing/redirects/rewrites-proxies/
|
||||
if !redirect.Force {
|
||||
path := filepath.Clean(strings.TrimPrefix(requestURI, u.Path))
|
||||
fi, err := f.c.hugo().BaseFs.PublishFs.Stat(path)
|
||||
if root != "" {
|
||||
path = filepath.Join(root, path)
|
||||
}
|
||||
fs := f.c.publishDirServerFs
|
||||
|
||||
fi, err := fs.Stat(path)
|
||||
|
||||
if err == nil {
|
||||
if fi.IsDir() {
|
||||
// There will be overlapping directories, so we
|
||||
// need to check for a file.
|
||||
_, err = f.c.hugo().BaseFs.PublishFs.Stat(filepath.Join(path, "index.html"))
|
||||
_, err = fs.Stat(filepath.Join(path, "index.html"))
|
||||
doRedirect = err != nil
|
||||
} else {
|
||||
doRedirect = false
|
||||
@@ -426,15 +433,28 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
|
||||
}
|
||||
|
||||
if doRedirect {
|
||||
if redirect.Status == 200 {
|
||||
switch redirect.Status {
|
||||
case 404:
|
||||
w.WriteHeader(404)
|
||||
file, err := fs.Open(filepath.FromSlash(strings.TrimPrefix(redirect.To, u.Path)))
|
||||
if err == nil {
|
||||
defer file.Close()
|
||||
io.Copy(w, file)
|
||||
} else {
|
||||
fmt.Fprintln(w, "<h1>Page Not Found</h1>")
|
||||
}
|
||||
return
|
||||
case 200:
|
||||
if r2 := f.rewriteRequest(r, strings.TrimPrefix(redirect.To, u.Path)); r2 != nil {
|
||||
requestURI = redirect.To
|
||||
r = r2
|
||||
}
|
||||
} else {
|
||||
fallthrough
|
||||
default:
|
||||
w.Header().Set("Content-Type", "")
|
||||
http.Redirect(w, r, redirect.To, redirect.Status)
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+104
-15
@@ -41,12 +41,43 @@ func TestServerPanicOnConfigError(t *testing.T) {
|
||||
linenos='table'
|
||||
`
|
||||
|
||||
r := runServerTest(c, 0, config)
|
||||
r := runServerTest(c,
|
||||
serverTestOptions{
|
||||
config: config,
|
||||
},
|
||||
)
|
||||
|
||||
c.Assert(r.err, qt.IsNotNil)
|
||||
c.Assert(r.err.Error(), qt.Contains, "cannot parse 'Highlight.LineNos' as bool:")
|
||||
}
|
||||
|
||||
func TestServer404(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
r := runServerTest(c,
|
||||
serverTestOptions{
|
||||
test404: true,
|
||||
getNumHomes: 1,
|
||||
},
|
||||
)
|
||||
|
||||
c.Assert(r.err, qt.IsNil)
|
||||
c.Assert(r.content404, qt.Contains, "404: 404 Page not found|Not Found.")
|
||||
}
|
||||
|
||||
// Issue 10287.
|
||||
func TestServerUnicode(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
r := runServerTest(c,
|
||||
serverTestOptions{
|
||||
pathsToGet: []string{"hügö/"},
|
||||
},
|
||||
)
|
||||
|
||||
c.Assert(r.err, qt.IsNil)
|
||||
c.Assert(r.pathsResults["hügö/"], qt.Contains, "This is hügö")
|
||||
}
|
||||
func TestServerFlags(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
@@ -81,7 +112,13 @@ baseURL="https://example.org"
|
||||
args = strings.Split(test.flag, "=")
|
||||
}
|
||||
|
||||
r := runServerTest(c, 1, config, args...)
|
||||
opts := serverTestOptions{
|
||||
config: config,
|
||||
args: args,
|
||||
getNumHomes: 1,
|
||||
}
|
||||
|
||||
r := runServerTest(c, opts)
|
||||
|
||||
test.assert(c, r)
|
||||
|
||||
@@ -101,6 +138,10 @@ func TestServerBugs(t *testing.T) {
|
||||
numservers int
|
||||
assert func(c *qt.C, r serverTestResult)
|
||||
}{
|
||||
{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
|
||||
c.Assert(r.err, qt.IsNil)
|
||||
c.Assert(r.homesContent[0], qt.Contains, "PostProcess: /foo.min.css")
|
||||
}},
|
||||
// Issue 9788
|
||||
{"PostProcess, memory", "", "", 1, func(c *qt.C, r serverTestResult) {
|
||||
c.Assert(r.err, qt.IsNil)
|
||||
@@ -140,7 +181,16 @@ baseURL="https://example.org"
|
||||
if test.flag != "" {
|
||||
args = strings.Split(test.flag, "=")
|
||||
}
|
||||
r := runServerTest(c, test.numservers, test.config, args...)
|
||||
|
||||
opts := serverTestOptions{
|
||||
config: test.config,
|
||||
getNumHomes: test.numservers,
|
||||
test404: true,
|
||||
args: args,
|
||||
}
|
||||
|
||||
r := runServerTest(c, opts)
|
||||
c.Assert(r.content404, qt.Contains, "404: 404 Page not found|Not Found.")
|
||||
test.assert(c, r)
|
||||
|
||||
})
|
||||
@@ -152,11 +202,25 @@ baseURL="https://example.org"
|
||||
type serverTestResult struct {
|
||||
err error
|
||||
homesContent []string
|
||||
content404 string
|
||||
publicDirnames map[string]bool
|
||||
pathsResults map[string]string
|
||||
}
|
||||
|
||||
func runServerTest(c *qt.C, getNumHomes int, config string, args ...string) (result serverTestResult) {
|
||||
dir := createSimpleTestSite(c, testSiteConfig{configTOML: config})
|
||||
type serverTestOptions struct {
|
||||
getNumHomes int
|
||||
test404 bool
|
||||
config string
|
||||
pathsToGet []string
|
||||
args []string
|
||||
}
|
||||
|
||||
func runServerTest(c *qt.C, opts serverTestOptions) serverTestResult {
|
||||
dir := createSimpleTestSite(c, testSiteConfig{configTOML: opts.config})
|
||||
result := serverTestResult{
|
||||
publicDirnames: make(map[string]bool),
|
||||
pathsResults: make(map[string]string),
|
||||
}
|
||||
|
||||
sp, err := helpers.FindAvailablePort()
|
||||
c.Assert(err, qt.IsNil)
|
||||
@@ -172,7 +236,7 @@ func runServerTest(c *qt.C, getNumHomes int, config string, args ...string) (res
|
||||
scmd := b.newServerCmdSignaled(stop)
|
||||
|
||||
cmd := scmd.getCommand()
|
||||
args = append([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)}, args...)
|
||||
args := append([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)}, opts.args...)
|
||||
cmd.SetArgs(args)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
@@ -184,16 +248,20 @@ func runServerTest(c *qt.C, getNumHomes int, config string, args ...string) (res
|
||||
return err
|
||||
})
|
||||
|
||||
if getNumHomes > 0 {
|
||||
if opts.getNumHomes > 0 {
|
||||
// Esp. on slow CI machines, we need to wait a little before the web
|
||||
// server is ready.
|
||||
time.Sleep(567 * time.Millisecond)
|
||||
result.homesContent = make([]string, getNumHomes)
|
||||
for i := 0; i < getNumHomes; i++ {
|
||||
wait := 567 * time.Millisecond
|
||||
if os.Getenv("CI") != "" {
|
||||
wait = 2 * time.Second
|
||||
}
|
||||
time.Sleep(wait)
|
||||
result.homesContent = make([]string, opts.getNumHomes)
|
||||
for i := 0; i < opts.getNumHomes; i++ {
|
||||
func() {
|
||||
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/", port+i))
|
||||
c.Check(err, qt.IsNil)
|
||||
c.Check(resp.StatusCode, qt.Equals, http.StatusOK)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(resp.StatusCode, qt.Equals, http.StatusOK)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
result.homesContent[i] = helpers.ReaderToString(resp.Body)
|
||||
@@ -202,6 +270,28 @@ func runServerTest(c *qt.C, getNumHomes int, config string, args ...string) (res
|
||||
}
|
||||
}
|
||||
|
||||
for _, path := range opts.pathsToGet {
|
||||
func() {
|
||||
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/%s", port, path))
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(resp.StatusCode, qt.Equals, http.StatusOK)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
result.pathsResults[path] = helpers.ReaderToString(resp.Body)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if opts.test404 {
|
||||
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/this-page-does-not-exist", port))
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(resp.StatusCode, qt.Equals, http.StatusNotFound)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
result.content404 = helpers.ReaderToString(resp.Body)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
select {
|
||||
@@ -210,15 +300,14 @@ func runServerTest(c *qt.C, getNumHomes int, config string, args ...string) (res
|
||||
}
|
||||
|
||||
pubFiles, err := os.ReadDir(filepath.Join(dir, "public"))
|
||||
c.Check(err, qt.IsNil)
|
||||
result.publicDirnames = make(map[string]bool)
|
||||
c.Assert(err, qt.IsNil)
|
||||
for _, f := range pubFiles {
|
||||
result.publicDirnames[f.Name()] = true
|
||||
}
|
||||
|
||||
result.err = wg.Wait()
|
||||
|
||||
return
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ package hugio
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
@@ -53,7 +51,7 @@ func CopyFile(fs afero.Fs, from, to string) error {
|
||||
|
||||
// CopyDir copies a directory.
|
||||
func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool) error {
|
||||
fi, err := os.Stat(from)
|
||||
fi, err := fs.Stat(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -67,7 +65,7 @@ func CopyDir(fs afero.Fs, from, to string, shouldCopy func(filename string) bool
|
||||
return err
|
||||
}
|
||||
|
||||
entries, _ := ioutil.ReadDir(from)
|
||||
entries, _ := afero.ReadDir(fs, from)
|
||||
for _, entry := range entries {
|
||||
fromFilename := filepath.Join(from, entry.Name())
|
||||
toFilename := filepath.Join(to, entry.Name())
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2022 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hugio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// HasBytesWriter is a writer that will set Match to true if the given pattern
|
||||
// is found in the stream.
|
||||
type HasBytesWriter struct {
|
||||
Match bool
|
||||
Pattern []byte
|
||||
|
||||
i int
|
||||
done bool
|
||||
buff []byte
|
||||
}
|
||||
|
||||
func (h *HasBytesWriter) Write(p []byte) (n int, err error) {
|
||||
if h.done {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
if len(h.buff) == 0 {
|
||||
h.buff = make([]byte, len(h.Pattern)*2)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if bytes.Contains(h.buff, h.Pattern) {
|
||||
h.Match = true
|
||||
h.done = true
|
||||
return len(p), nil
|
||||
}
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2022 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hugio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestHasBytesWriter(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
c := qt.New((t))
|
||||
|
||||
neww := func() (*HasBytesWriter, io.Writer) {
|
||||
var b bytes.Buffer
|
||||
|
||||
h := &HasBytesWriter{
|
||||
Pattern: []byte("__foo"),
|
||||
}
|
||||
return h, io.MultiWriter(&b, h)
|
||||
}
|
||||
|
||||
rndStr := func() string {
|
||||
return strings.Repeat("ab cfo", r.Intn(33))
|
||||
}
|
||||
|
||||
for i := 0; i < 22; i++ {
|
||||
h, w := neww()
|
||||
fmt.Fprintf(w, rndStr()+"abc __foobar"+rndStr())
|
||||
c.Assert(h.Match, qt.Equals, true)
|
||||
|
||||
h, w = neww()
|
||||
fmt.Fprintf(w, rndStr()+"abc __f")
|
||||
fmt.Fprintf(w, "oo bar"+rndStr())
|
||||
c.Assert(h.Match, qt.Equals, true)
|
||||
|
||||
h, w = neww()
|
||||
fmt.Fprintf(w, rndStr()+"abc __moo bar")
|
||||
c.Assert(h.Match, qt.Equals, false)
|
||||
}
|
||||
|
||||
h, w := neww()
|
||||
fmt.Fprintf(w, "__foo")
|
||||
c.Assert(h.Match, qt.Equals, true)
|
||||
}
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 102,
|
||||
PatchLevel: 0,
|
||||
Suffix: "-DEV",
|
||||
Minor: 103,
|
||||
PatchLevel: 1,
|
||||
Suffix: "",
|
||||
}
|
||||
|
||||
+30
-10
@@ -180,10 +180,15 @@ type Headers struct {
|
||||
}
|
||||
|
||||
type Redirect struct {
|
||||
From string
|
||||
To string
|
||||
From string
|
||||
To string
|
||||
|
||||
// HTTP status code to use for the redirect.
|
||||
// A status code of 200 will trigger a URL rewrite.
|
||||
Status int
|
||||
Force bool
|
||||
|
||||
// Forcode redirect, even if original request path exists.
|
||||
Force bool
|
||||
}
|
||||
|
||||
func (r Redirect) IsZero() bool {
|
||||
@@ -200,16 +205,31 @@ func DecodeServer(cfg Provider) (*Server, error) {
|
||||
_ = mapstructure.WeakDecode(m, s)
|
||||
|
||||
for i, redir := range s.Redirects {
|
||||
// Get it in line with the Hugo server.
|
||||
redir.To = strings.TrimSuffix(redir.To, "index.html")
|
||||
if !strings.HasPrefix(redir.To, "https") && !strings.HasSuffix(redir.To, "/") {
|
||||
// There are some tricky infinite loop situations when dealing
|
||||
// when the target does not have a trailing slash.
|
||||
// This can certainly be handled better, but not time for that now.
|
||||
return nil, fmt.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To)
|
||||
// Get it in line with the Hugo server for OK responses.
|
||||
// We currently treat the 404 as a special case, they are always "ugly", so keep them as is.
|
||||
if redir.Status != 404 {
|
||||
redir.To = strings.TrimSuffix(redir.To, "index.html")
|
||||
if !strings.HasPrefix(redir.To, "https") && !strings.HasSuffix(redir.To, "/") {
|
||||
// There are some tricky infinite loop situations when dealing
|
||||
// when the target does not have a trailing slash.
|
||||
// This can certainly be handled better, but not time for that now.
|
||||
return nil, fmt.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To)
|
||||
}
|
||||
}
|
||||
s.Redirects[i] = redir
|
||||
}
|
||||
|
||||
if len(s.Redirects) == 0 {
|
||||
// Set up a default redirect for 404s.
|
||||
s.Redirects = []Redirect{
|
||||
{
|
||||
From: "**",
|
||||
To: "/404.html",
|
||||
Status: 404,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func TestToTOML(t *testing.T) {
|
||||
got := DefaultConfig.ToTOML()
|
||||
|
||||
c.Assert(got, qt.Equals,
|
||||
"[security]\n enableInlineShortcodes = false\n [security.exec]\n allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^(PATH|PATHEXT|APPDATA|TMP|TEMP|TERM)$']\n\n [security.funcs]\n getenv = ['^HUGO_']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^(PATH|PATHEXT|APPDATA|TMP|TEMP|TERM)$']\n\n [security.funcs]\n getenv = ['^HUGO_']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -52,7 +52,7 @@ draft: true
|
||||
|
||||
// NewContent creates a new content file in h (or a full bundle if the archetype is a directory)
|
||||
// in targetPath.
|
||||
func NewContent(h *hugolib.HugoSites, kind, targetPath string) error {
|
||||
func NewContent(h *hugolib.HugoSites, kind, targetPath string, force bool) error {
|
||||
if h.BaseFs.Content.Dirs == nil {
|
||||
return errors.New("no existing content directory configured for this project")
|
||||
}
|
||||
@@ -76,6 +76,7 @@ func NewContent(h *hugolib.HugoSites, kind, targetPath string) error {
|
||||
|
||||
kind: kind,
|
||||
targetPath: targetPath,
|
||||
force: force,
|
||||
}
|
||||
|
||||
ext := paths.Ext(targetPath)
|
||||
@@ -132,6 +133,7 @@ type contentBuilder struct {
|
||||
kind string
|
||||
isDir bool
|
||||
dirMap archetypeMap
|
||||
force bool
|
||||
}
|
||||
|
||||
func (b *contentBuilder) buildDir() error {
|
||||
@@ -145,7 +147,7 @@ func (b *contentBuilder) buildDir() error {
|
||||
|
||||
for _, fi := range b.dirMap.contentFiles {
|
||||
targetFilename := filepath.Join(b.targetPath, strings.TrimPrefix(fi.Meta().Path, b.archetypeFilename))
|
||||
abs, err := b.cf.CreateContentPlaceHolder(targetFilename)
|
||||
abs, err := b.cf.CreateContentPlaceHolder(targetFilename, b.force)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -218,7 +220,7 @@ func (b *contentBuilder) buildDir() error {
|
||||
}
|
||||
|
||||
func (b *contentBuilder) buildFile() (string, error) {
|
||||
contentPlaceholderAbsFilename, err := b.cf.CreateContentPlaceHolder(b.targetPath)
|
||||
contentPlaceholderAbsFilename, err := b.cf.CreateContentPlaceHolder(b.targetPath, b.force)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
+36
-9
@@ -82,7 +82,7 @@ func TestNewContentFromFile(t *testing.T) {
|
||||
cfg, fs := newTestCfg(c, mm)
|
||||
h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
|
||||
c.Assert(err, qt.IsNil)
|
||||
err = create.NewContent(h, cas.kind, cas.path)
|
||||
err = create.NewContent(h, cas.kind, cas.path, false)
|
||||
|
||||
if b, ok := cas.expected.(bool); ok && !b {
|
||||
if !b {
|
||||
@@ -145,7 +145,7 @@ i18n: {{ T "hugo" }}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(len(h.Sites), qt.Equals, 2)
|
||||
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/my-post"), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNil)
|
||||
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo1.json")), `hugo1: {{ printf "no template handling in here" }}`)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo2.xml")), `hugo2: {{ printf "no template handling in here" }}`)
|
||||
@@ -157,7 +157,7 @@ i18n: {{ T "hugo" }}
|
||||
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/pages/bio.md")), `File: bio.md`, `Site Lang: en`, `Name: Bio`)
|
||||
|
||||
c.Assert(create.NewContent(h, "my-theme-bundle", "post/my-theme-post"), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "my-theme-bundle", "post/my-theme-post", false), qt.IsNil)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-theme-post/index.md")), `File: index.md`, `Site Lang: en`, `Name: My Theme Post`, `i18n: Hugo Rocks!`)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-theme-post/resources/hugo1.json")), `hugo1: {{ printf "no template handling in here" }}`)
|
||||
}
|
||||
@@ -187,19 +187,19 @@ site RegularPages: {{ len site.RegularPages }}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(len(h.Sites), qt.Equals, 2)
|
||||
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/my-post"), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNil)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/index.md")), `site RegularPages: 10`)
|
||||
|
||||
// Default bundle archetype
|
||||
c.Assert(create.NewContent(h, "", "post/my-post2"), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "", "post/my-post2", false), qt.IsNil)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post2/index.md")), `default archetype index.md`)
|
||||
|
||||
// Regular file with bundle kind.
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/foo.md"), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/foo.md", false), qt.IsNil)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/foo.md")), `draft: true`)
|
||||
|
||||
// Regular files should fall back to the default archetype (we have no regular file archetype).
|
||||
c.Assert(create.NewContent(h, "my-bundle", "mypage.md"), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "my-bundle", "mypage.md", false), qt.IsNil)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "mypage.md")), `draft: true`)
|
||||
|
||||
}
|
||||
@@ -237,7 +237,7 @@ i18n: {{ T "hugo" }}
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(len(h.Sites), qt.Equals, 2)
|
||||
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/my-post"), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNil)
|
||||
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo1.json")), `hugo1: {{ printf "no template handling in here" }}`)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo2.xml")), `hugo2: {{ printf "no template handling in here" }}`)
|
||||
@@ -247,11 +247,38 @@ i18n: {{ T "hugo" }}
|
||||
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/pages/bio.md")), `File: bio.md`, `Name: Bio`)
|
||||
|
||||
c.Assert(create.NewContent(h, "my-theme-bundle", "post/my-theme-post"), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "my-theme-bundle", "post/my-theme-post", false), qt.IsNil)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-theme-post/index.md")), `File: index.md`, `Name: My Theme Post`, `i18n: Hugo Rocks!`)
|
||||
cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-theme-post/resources/hugo1.json")), `hugo1: {{ printf "no template handling in here" }}`)
|
||||
}
|
||||
|
||||
func TestNewContentForce(t *testing.T) {
|
||||
mm := afero.NewMemMapFs()
|
||||
c := qt.New(t)
|
||||
|
||||
archetypeDir := filepath.Join("archetypes", "my-bundle")
|
||||
c.Assert(mm.MkdirAll(archetypeDir, 0o755), qt.IsNil)
|
||||
c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), []byte(""), 0o755), qt.IsNil)
|
||||
c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.nn.md"), []byte(""), 0o755), qt.IsNil)
|
||||
|
||||
c.Assert(initFs(mm), qt.IsNil)
|
||||
cfg, fs := newTestCfg(c, mm)
|
||||
|
||||
h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(len(h.Sites), qt.Equals, 2)
|
||||
|
||||
// from file
|
||||
c.Assert(create.NewContent(h, "post", "post/my-post.md", false), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "post", "post/my-post.md", false), qt.IsNotNil)
|
||||
c.Assert(create.NewContent(h, "post", "post/my-post.md", true), qt.IsNil)
|
||||
|
||||
// from dir
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNil)
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNotNil)
|
||||
c.Assert(create.NewContent(h, "my-bundle", "post/my-post", true), qt.IsNil)
|
||||
}
|
||||
|
||||
func initFs(fs afero.Fs) error {
|
||||
perm := os.FileMode(0o755)
|
||||
var err error
|
||||
|
||||
Vendored
+19
-1
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/postpub"
|
||||
|
||||
"github.com/gohugoio/hugo/metrics"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
@@ -78,6 +79,10 @@ type Deps struct {
|
||||
// All the output formats available for the current site.
|
||||
OutputFormatsConfig output.Formats
|
||||
|
||||
// FilenameHasPostProcessPrefix is a set of filenames in /public that
|
||||
// contains a post-processing prefix.
|
||||
FilenameHasPostProcessPrefix []string
|
||||
|
||||
templateProvider ResourceProvider
|
||||
WithTemplate func(templ tpl.TemplateManager) error `json:"-"`
|
||||
|
||||
@@ -202,6 +207,7 @@ func New(cfg DepsCfg) (*Deps, error) {
|
||||
var (
|
||||
logger = cfg.Logger
|
||||
fs = cfg.Fs
|
||||
d *Deps
|
||||
)
|
||||
|
||||
if cfg.TemplateProvider == nil {
|
||||
@@ -239,6 +245,18 @@ func New(cfg DepsCfg) (*Deps, error) {
|
||||
}
|
||||
execHelper := hexec.New(securityConfig)
|
||||
|
||||
var filenameHasPostProcessPrefixMu sync.Mutex
|
||||
cb := func(name string, match bool) {
|
||||
if !match {
|
||||
return
|
||||
}
|
||||
filenameHasPostProcessPrefixMu.Lock()
|
||||
d.FilenameHasPostProcessPrefix = append(d.FilenameHasPostProcessPrefix, name)
|
||||
filenameHasPostProcessPrefixMu.Unlock()
|
||||
|
||||
}
|
||||
fs.PublishDir = hugofs.NewHasBytesReceiver(fs.PublishDir, cb, []byte(postpub.PostProcessPrefix))
|
||||
|
||||
ps, err := helpers.NewPathSpec(fs, cfg.Language, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create PathSpec: %w", err)
|
||||
@@ -274,7 +292,7 @@ func New(cfg DepsCfg) (*Deps, error) {
|
||||
|
||||
logDistinct := helpers.NewDistinctLogger(logger)
|
||||
|
||||
d := &Deps{
|
||||
d = &Deps{
|
||||
Fs: fs,
|
||||
Log: ignorableLogger,
|
||||
LogDistinct: logDistinct,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/.idea
|
||||
/.vscode
|
||||
/public
|
||||
/dist
|
||||
node_modules
|
||||
nohup.out
|
||||
.DS_Store
|
||||
|
||||
+13
-14
@@ -1,19 +1,18 @@
|
||||
[[banners]]
|
||||
name = "Linode"
|
||||
link = "https://www.linode.com/"
|
||||
logo = "/images/sponsors/linode-logo_standard_light_medium.png"
|
||||
copy = ""
|
||||
name = "Linode"
|
||||
link = "https://www.linode.com/"
|
||||
logo = "/images/sponsors/linode-logo_standard_light_medium.png"
|
||||
copy = ""
|
||||
|
||||
[[banners]]
|
||||
name = "eSolia"
|
||||
link = "https://esolia.com/post/why-did-esolia-choose-hugo/"
|
||||
logo = "/images/sponsors/esolia-logo.svg"
|
||||
copy = ""
|
||||
|
||||
name = "eSolia"
|
||||
link = "https://esolia.com/post/why-did-esolia-choose-hugo/"
|
||||
logo = "/images/sponsors/esolia-logo.svg"
|
||||
copy = ""
|
||||
|
||||
[[banners]]
|
||||
name = "Brave"
|
||||
link = "https://brave.com/"
|
||||
logo = "/images/sponsors/brave-logo.svg"
|
||||
bgcolor = "#5A2FA1"
|
||||
copy = ""
|
||||
name = "BEP Consulting"
|
||||
link = "https://bep.consulting"
|
||||
logo = "/images/sponsors/bep-consulting.svg"
|
||||
bgcolor = "#004887"
|
||||
copy = ""
|
||||
|
||||
@@ -70,14 +70,6 @@
|
||||
{{ partial "hooks/before-body-end" . }}
|
||||
|
||||
|
||||
{{ if .Page.Store.Get "hasMermaid" }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||||
<script>
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
</script>
|
||||
{{ end }}
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<svg width="158" height="160" viewBox="0 0 158 160" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.32 103V27.64H17.664V54.616C18.752 54.04 20 53.56 21.408 53.176C22.816 52.728 24.192 52.376 25.536 52.12C26.88 51.864 28.192 51.672 29.472 51.544C30.816 51.352 32.032 51.256 33.12 51.256C35.872 51.256 38.24 51.704 40.224 52.6C42.272 53.432 43.904 54.584 45.12 56.056C46.336 57.464 47.296 59.32 48 61.624C48.704 63.928 49.152 66.328 49.344 68.824C49.6 71.32 49.728 74.296 49.728 77.752C49.728 86.008 48.448 92.376 45.888 96.856C43.328 101.336 39.072 103.576 33.12 103.576C30.368 103.576 27.584 103 24.768 101.848C21.952 100.696 19.616 99.352 17.76 97.816L15.36 103H4.32ZM17.76 88.984C21.024 91.032 24.992 92.056 29.664 92.056C31.136 92.056 32.352 91.64 33.312 90.808C34.272 89.912 35.008 88.376 35.52 86.2C36.032 83.96 36.288 80.984 36.288 77.272C36.288 73.048 36 69.784 35.424 67.48C34.912 65.176 34.208 63.672 33.312 62.968C32.416 62.2 31.2 61.816 29.664 61.816C27.296 61.816 25.152 61.976 23.232 62.296C21.312 62.552 19.488 63.032 17.76 63.736V88.984ZM56.8958 77.272C56.8958 72.856 57.1518 69.176 57.6638 66.232C58.2398 63.224 59.0398 60.76 60.0638 58.84C61.0878 56.856 62.5278 55.352 64.3838 54.328C66.2398 53.304 68.2878 52.6 70.5278 52.216C72.8318 51.832 75.7118 51.64 79.1678 51.64C81.4718 51.64 83.4238 51.704 85.0238 51.832C86.6238 51.896 88.2238 52.088 89.8238 52.408C91.4878 52.664 92.8318 53.08 93.8558 53.656C94.8798 54.168 95.8718 54.904 96.8318 55.864C97.7918 56.76 98.5278 57.88 99.0398 59.224C99.6158 60.504 100.032 62.072 100.288 63.928C100.608 65.72 100.768 67.8 100.768 70.168C100.768 74.648 99.5838 77.912 97.2158 79.96C94.8478 82.008 91.2958 83.032 86.5598 83.032H70.3358C70.3358 85.976 70.7518 88.184 71.5838 89.656C72.4798 91.064 73.5678 91.928 74.8478 92.248C76.1918 92.568 78.2718 92.728 81.0878 92.728C88.9598 92.728 95.1998 92.376 99.8078 91.672V100.792C95.0078 102.584 86.3038 103.48 73.6958 103.48C70.4318 103.48 67.7118 103 65.5358 102.04C63.4238 101.08 61.7278 99.512 60.4478 97.336C59.1678 95.16 58.2398 92.472 57.6638 89.272C57.1518 86.072 56.8958 82.072 56.8958 77.272ZM70.3358 73.336H83.1038C85.9198 73.336 87.3278 72.056 87.3278 69.496C87.3278 66.296 86.8798 64.248 85.9838 63.352C85.1518 62.392 83.1358 61.912 79.9358 61.912C77.8878 61.912 76.3518 62.008 75.3278 62.2C74.3678 62.328 73.4078 62.776 72.4478 63.544C71.5518 64.248 70.9438 65.368 70.6238 66.904C70.3678 68.44 70.2718 70.584 70.3358 73.336ZM108.101 128.92V52.024H119.141L121.445 57.112C123.301 55.64 125.637 54.328 128.453 53.176C131.333 52.024 134.149 51.448 136.901 51.448C139.397 51.448 141.573 51.864 143.429 52.696C145.349 53.528 146.917 54.68 148.133 56.152C149.413 57.624 150.469 59.448 151.301 61.624C152.133 63.8 152.709 66.168 153.029 68.728C153.349 71.288 153.509 74.136 153.509 77.272C153.509 79.896 153.413 82.264 153.221 84.376C153.093 86.488 152.805 88.6 152.357 90.712C151.909 92.76 151.269 94.552 150.437 96.088C149.669 97.624 148.677 99 147.461 100.216C146.309 101.368 144.837 102.264 143.045 102.904C141.253 103.48 139.205 103.768 136.901 103.768C134.597 103.768 132.005 103.48 129.125 102.904C126.245 102.264 123.685 101.464 121.445 100.504V128.92H108.101ZM121.445 91.288C124.773 92.568 128.773 93.208 133.445 93.208C134.981 93.208 136.197 92.856 137.093 92.152C137.989 91.384 138.693 89.848 139.205 87.544C139.781 85.24 140.069 82.008 140.069 77.848C140.069 72.344 139.525 68.504 138.437 66.328C137.349 64.088 135.685 62.968 133.445 62.968C128.709 62.968 124.709 63.992 121.445 66.04V91.288Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
+4
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 8.0 KiB |
@@ -1 +1 @@
|
||||
# github.com/gohugoio/gohugoioTheme v0.0.0-20220228085601-7cfbda06d135
|
||||
# github.com/gohugoio/gohugoioTheme v0.0.0-20220912070954-88dcaf003b4d
|
||||
|
||||
@@ -4,7 +4,6 @@ linktitle: Comments
|
||||
description: Hugo ships with an internal Disqus template, but this isn't the only commenting system that will work with your new Hugo website.
|
||||
date: 2017-02-01
|
||||
publishdate: 2017-02-01
|
||||
lastmod: 2017-03-09
|
||||
keywords: [sections,content,organization]
|
||||
categories: [project organization, fundamentals]
|
||||
menu:
|
||||
@@ -60,7 +59,7 @@ These are some alternatives to Disqus:
|
||||
* [Muut](https://muut.com/)
|
||||
* [Remark42](https://remark42.com/) (Open source, Golang, Easy to run docker)
|
||||
* [Staticman](https://staticman.net/)
|
||||
* [Talkyard](https://www.talkyard.io/blog-comments) (Open source, & serverless hosting)
|
||||
* [Talkyard](https://blog-comments.talkyard.io/) (Open source, & serverless hosting)
|
||||
* [Utterances](https://utteranc.es/) (Open source, GitHub comments widget built on GitHub issues)
|
||||
|
||||
[configuration]: /getting-started/configuration/
|
||||
|
||||
@@ -28,7 +28,11 @@ content/
|
||||
└── sunset.jpg <-- page resource
|
||||
```
|
||||
|
||||
To access an image as a page resource:
|
||||
## The Image Resource
|
||||
|
||||
The `image` resource gives you access to image-specific attributes like the picture's `Width` and `Height`, as well as powerful processing methods and filters. More on that below.
|
||||
|
||||
Note that the `image` resource can also be retrieved from a [global resource]({{< relref "/hugo-pipes/introduction#from-file-to-resource" >}})
|
||||
|
||||
```go-html-template
|
||||
{{ $image := .Resources.GetMatch "sunset.jpg" }}
|
||||
|
||||
@@ -4,7 +4,6 @@ linktitle: Multilingual
|
||||
description: Hugo supports the creation of websites with multiple languages side by side.
|
||||
date: 2017-01-10
|
||||
publishdate: 2017-01-10
|
||||
lastmod: 2017-01-10
|
||||
categories: [content management]
|
||||
keywords: [multilingual,i18n, internationalization]
|
||||
menu:
|
||||
@@ -335,13 +334,13 @@ This article has 101 words.
|
||||
|
||||
### Query a singular/plural translation
|
||||
|
||||
In order to meet singular/plural requirement, you must pass a dictionary (map) with a numeric `.Count` property to the `i18n` function. The below example uses `.ReadingTime` variable which has a built-in `.Count` property.
|
||||
In other to meet singular/plural requirement, you must pass a dictionary (map) with a numeric `.Count` property to the `i18n` function. The below example uses `.ReadingTime` variable which has a built-in `.Count` property.
|
||||
|
||||
```go-html-template
|
||||
{{ i18n "readingTime" .ReadingTime }}
|
||||
```
|
||||
|
||||
The function will read `.Count` from `.ReadingTime` and evaluate where the number is singular (`one`) or plural (`other`). After that, it will pass to `readingTime` id:
|
||||
The function will read `.Count` from `.ReadingTime` and evaluate whether the number is singular (`one`) or plural (`other`). After that, it will pass to `readingTime` id in `i18n/en-US.toml` file:
|
||||
|
||||
{{< code-toggle file="i18n/en-US" >}}
|
||||
[readingTime]
|
||||
@@ -349,7 +348,7 @@ one = "One minute to read"
|
||||
other = "{{.Count}} minutes to read"
|
||||
{{< /code-toggle >}}
|
||||
|
||||
Assume `.ReadingTime.Count` in the context has value of 525600. The result will be:
|
||||
Assuming `.ReadingTime.Count` in the context has value is 525600. The result will be:
|
||||
|
||||
```text
|
||||
525600 minutes to read
|
||||
@@ -361,7 +360,7 @@ If `.ReadingTime.Count` in the context has value is 1. The result is:
|
||||
One minute to read
|
||||
```
|
||||
|
||||
In case you need to pass custom data: (`(dict "Count" 25)` is minimum requirement)
|
||||
In case you need to pass a custom data: (`(dict "Count" numeric_value_only)` is minimum requirement)
|
||||
|
||||
```go-html-template
|
||||
{{ i18n "readingTime" (dict "Count" 25 "FirstArgument" true "SecondArgument" false "Etc" "so on, so far") }}
|
||||
@@ -507,6 +506,40 @@ The rendering of the main navigation works as usual. `.Site.Menus` will just con
|
||||
</ul>
|
||||
```
|
||||
|
||||
### Dynamically localizing menus with i18n
|
||||
While customizing menus per language is useful, your config file can become hard to maintain if you have a lot of languages
|
||||
|
||||
If your menus are the same in all languages (ie. if the only thing that changes is the translated name) you can use the `.Identifier` as a translation key for the menu name:
|
||||
|
||||
{{< code-toggle file="config" >}}
|
||||
[[menu.main]]
|
||||
name = "About me"
|
||||
url = "about"
|
||||
weight = 1
|
||||
identifier = "about"
|
||||
{{< /code-toggle >}}
|
||||
|
||||
You now need to specify the translations for the menu keys in the i18n files:
|
||||
|
||||
{{< code file="i18n/pt.toml" >}}
|
||||
[about]
|
||||
other="Sobre mim"
|
||||
{{< /code >}}
|
||||
|
||||
And do the appropriate changes in the menu code to use the `i18n` tag with the `.Identifier` as a key. You will also note that here we are using a `default` to fall back to `.Name`, in case the `.Identifier` key is also not present in the language specified in the `defaultContentLanguage` configuration.
|
||||
|
||||
{{< code file="layouts/partials/menu.html" >}}
|
||||
<ul>
|
||||
{{- $currentPage := . -}}
|
||||
{{ range .Site.Menus.main -}}
|
||||
<li class="{{ if $currentPage.IsMenuCurrent "main" . }}active{{ end }}">
|
||||
<a href="{{ .URL | absLangURL }}">{{ i18n .Identifier | default .Name}}</a>
|
||||
</li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
{{< /code >}}
|
||||
|
||||
|
||||
## Missing Translations
|
||||
|
||||
If a string does not have a translation for the current language, Hugo will use the value from the default language. If no default value is set, an empty string will be shown.
|
||||
@@ -535,6 +568,12 @@ To support Multilingual mode in your themes, some considerations must be taken f
|
||||
|
||||
If there is more than one language defined, the `LanguagePrefix` variable will equal `/en` (or whatever your `CurrentLanguage` is). If not enabled, it will be an empty string (and is therefore harmless for single-language Hugo websites).
|
||||
|
||||
|
||||
## Generate multilingual content with `hugo new`
|
||||
Currently, `hugo new` is not ready to support generating multilingual content. But there is a [proposal topic](https://github.com/gohugoio/hugo/issues/7732) about this in Github issue to discuss how it should work.
|
||||
|
||||
|
||||
|
||||
[abslangurl]: /functions/abslangurl
|
||||
[config]: /getting-started/configuration/
|
||||
[contenttemplate]: /templates/single-page-templates/
|
||||
|
||||
@@ -64,26 +64,27 @@ content/
|
||||
In the above example `content/` directory, there are four leaf
|
||||
bundles:
|
||||
|
||||
about
|
||||
`about`
|
||||
: This leaf bundle is at the root level (directly under
|
||||
`content` directory) and has only the `index.md`.
|
||||
|
||||
my-post
|
||||
`my-post`
|
||||
: This leaf bundle has the `index.md`, two other content
|
||||
Markdown files and two image files.
|
||||
|
||||
image1
|
||||
: This image is a page resource of `my-post`
|
||||
- image1, image2:
|
||||
These images are page resources of `my-post`
|
||||
and only available in `my-post/index.md` resources.
|
||||
|
||||
image2
|
||||
: This image is a page resource of `my-post`
|
||||
and only available in `my-post/index.md` resources.
|
||||
- content1, content2:
|
||||
These content files are page resources of `my-post`
|
||||
and only available in `my-post/index.md` resources.
|
||||
They will **not** be rendered as individual pages.
|
||||
|
||||
my-other-post
|
||||
`my-other-post`
|
||||
: This leaf bundle has only the `index.md`.
|
||||
|
||||
another-leaf-bundle
|
||||
`another-leaf-bundle`
|
||||
: This leaf bundle is nested under couple of
|
||||
directories. This bundle also has only the `index.md`.
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ name
|
||||
: Sets the value returned in `Name`.
|
||||
|
||||
{{% warning %}}
|
||||
The methods `Match` and `GetMatch` use `Name` to match the resources.
|
||||
The methods `Match`, `Get` and `GetMatch` use `Name` to match the resources.
|
||||
{{%/ warning %}}
|
||||
|
||||
title
|
||||
|
||||
@@ -80,6 +80,20 @@ func GetTitleFunc(style string) func(s string) string {
|
||||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
## Highlight Hugo/GO Template Code
|
||||
|
||||
For highlighting Hugo/GO template code on your page, add `/*` after the opening double curly braces and `*/` before closing curly braces.
|
||||
|
||||
``` go
|
||||
{{</*/* myshortcode */*/>}}
|
||||
```
|
||||
|
||||
Gives this:
|
||||
|
||||
``` go
|
||||
{{</* myshortcode */>}}
|
||||
```
|
||||
|
||||
## Highlight Template Func
|
||||
|
||||
See [Highlight](/functions/highlight/).
|
||||
|
||||
@@ -14,8 +14,6 @@ signature: [".RenderString MARKUP"]
|
||||
|
||||
`.RenderString` is a method on `Page` that renders some markup to HTML using the content renderer defined for that page (if not set in the options).
|
||||
|
||||
*Note* that this method does not parse and render shortcodes.
|
||||
|
||||
The method takes an optional map argument with these options:
|
||||
|
||||
display ("inline")
|
||||
|
||||
@@ -18,7 +18,7 @@ deprecated: false
|
||||
aliases: []
|
||||
---
|
||||
|
||||
This translates a piece of content based on your `i18n/en-US.yaml` (and similar) files. You can use the [go-i18n](https://github.com/nicksnyder/go-i18n) tools to manage your translations. The translations can exist in both the theme and at the root of your repository.
|
||||
This translates a piece of content based on your `i18n/en-US.toml` files. You can use the [go-i18n](https://github.com/nicksnyder/go-i18n) tools to manage your translations. The translations can exist in both the theme and at the root of your repository.
|
||||
|
||||
```
|
||||
{{ i18n "translation_id" }}
|
||||
@@ -28,6 +28,27 @@ This translates a piece of content based on your `i18n/en-US.yaml` (and similar)
|
||||
`T` is an alias to `i18n`. E.g. `{{ T "translation_id" }}`.
|
||||
{{% /note %}}
|
||||
|
||||
### Query a flexible translation with variables
|
||||
|
||||
Often you will want to use the page variables in the translation strings. To do so, pass the `.` context when calling `i18n`:
|
||||
|
||||
```
|
||||
{{ i18n "wordCount" . }}
|
||||
```
|
||||
|
||||
The function will pass the `.` context to the `"wordCount"` id:
|
||||
|
||||
{{< code-toggle file="i18n/en-US" >}}
|
||||
[wordCount]
|
||||
other = "This article has {{ .WordCount }} words."
|
||||
{{< /code-toggle >}}
|
||||
|
||||
Assume `.WordCount` in the context has value is 101. The result will be:
|
||||
|
||||
```
|
||||
This article has 101 words.
|
||||
```
|
||||
|
||||
For more information about string translations, see [Translation of Strings in Multilingual Mode][multistrings].
|
||||
|
||||
[multistrings]: /content-management/multilingual/#translation-of-strings
|
||||
|
||||
@@ -218,6 +218,8 @@ Also see the [Filter Method](/content-management/image-processing/#filter).
|
||||
|
||||
Parses the image and returns the height, width, and color model.
|
||||
|
||||
The `imageConfig` function takes a single parameter, a file path (_string_) relative to the _project's root directory_, with or without a leading slash.
|
||||
|
||||
{{% funcsig %}}
|
||||
images.ImageConfig PATH
|
||||
{{% /funcsig %}}
|
||||
|
||||
@@ -23,7 +23,9 @@ toc: false
|
||||
One use case is the concatenation of elements in combination with the [`delimit` function][]:
|
||||
|
||||
{{< code file="slice.html" >}}
|
||||
{{ delimit (slice "foo" "bar" "buzz") ", " }}
|
||||
{{ $sliceOfStrings := slice "foo" "bar" "buzz" }}
|
||||
<!-- returns the slice [ "foo", "bar", "buzz"] -->
|
||||
{{ delimit ($sliceOfStrings) ", " }}
|
||||
<!-- returns the string "foo, bar, buzz" -->
|
||||
{{< /code >}}
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ This is also very useful to use as `OR` filters when combined with where:
|
||||
|
||||
```
|
||||
{{ $pages := where .Site.RegularPages "Type" "not in" (slice "page" "about") }}
|
||||
{{ $pages := $pages | union (where .Site.RegularPages "Params.pinned" true) }}
|
||||
{{ $pages := $pages | intersect (where .Site.RegularPages "Params.images" "!=" nil) }}
|
||||
{{ $pages = $pages | union (where .Site.RegularPages "Params.pinned" true) }}
|
||||
{{ $pages = $pages | intersect (where .Site.RegularPages "Params.images" "!=" nil) }}
|
||||
```
|
||||
|
||||
The above fetches regular pages not of `page` or `about` type unless they are pinned. And finally, we exclude all pages with no `images` set in Page params.
|
||||
|
||||
@@ -4,7 +4,6 @@ linktitle: Configuration
|
||||
description: How to configure your Hugo site.
|
||||
date: 2013-07-01
|
||||
publishdate: 2017-01-02
|
||||
lastmod: 2017-03-05
|
||||
categories: [getting started,fundamentals]
|
||||
keywords: [configuration,toml,yaml,json]
|
||||
menu:
|
||||
@@ -18,7 +17,6 @@ aliases: [/overview/source-directory/,/overview/configuration/]
|
||||
toc: true
|
||||
---
|
||||
|
||||
|
||||
## Configuration File
|
||||
|
||||
Hugo uses the `config.toml`, `config.yaml`, or `config.json` (if found in the
|
||||
@@ -77,6 +75,27 @@ foo = "bar"
|
||||
```
|
||||
|
||||
Considering the structure above, when running `hugo --environment staging`, Hugo will use every settings from `config/_default` and merge `staging`'s on top of those.
|
||||
|
||||
Let's take an example to understand this better. Let's say you are using Google Analytics for your website. This requires you to specify `googleAnalytics = "G-XXXXXXXX"` in `config.toml`. Now consider the following scenario:
|
||||
- You don't want the Analytics code to be loaded in development i.e. in your `localhost`
|
||||
- You want to use separate googleAnalytics IDs for your production & staging environments (say):
|
||||
- `G-PPPPPPPP` for production
|
||||
- `G-SSSSSSSS` for staging
|
||||
|
||||
This is how you need to configure your `config.toml` files considering the above scenario:
|
||||
1. In `_default/config.toml` you don't need to mention `googleAnalytics` parameter at all. This ensures that no Google Analytics code is loaded in your development server i.e. when you run `hugo serve`. This works since, by default Hugo sets `Environment=development` when you run `hugo serve` which uses the config files from `_default` folder
|
||||
2. In `production/config.toml` you just need to have one line:
|
||||
|
||||
```googleAnalytics = "G-PPPPPPPP"```
|
||||
|
||||
You don't need to mention all other parameters like `title`, `baseURL`, `theme` etc. again in this config file. You need to mention only those parameters which are different or new for the production environment. This is due to the fact that Hugo is going to __merge__ this on top of `_default/config.toml`. Now when you run `hugo` (build command), by default hugo sets `Environment=production`, so the `G-PPPPPPPP` analytics code will be there in your production website
|
||||
3. Similarly in `staging/config.toml` you just need to have one line:
|
||||
|
||||
```googleAnalytics = "G-SSSSSSSS"```
|
||||
|
||||
Now you need to tell Hugo that you are using the staging environment. So your build command should be `hugo --environment staging` which will load the `G-SSSSSSSS` analytics code in your staging website
|
||||
|
||||
|
||||
{{% note %}}
|
||||
Default environments are __development__ with `hugo server` and __production__ with `hugo`.
|
||||
{{%/ note %}}
|
||||
@@ -149,7 +168,7 @@ See [Configure File Caches](#configure-file-caches)
|
||||
|
||||
{{< new-in "0.86.0" >}}
|
||||
|
||||
Pass down down default configuration values (front matter) to pages in the content tree. The options in site config is the same as in page front matter, see [Front Matter Cascade](/content-management/front-matter#front-matter-cascade).
|
||||
Pass down default configuration values (front matter) to pages in the content tree. The options in site config is the same as in page front matter, see [Front Matter Cascade](/content-management/front-matter#front-matter-cascade).
|
||||
|
||||
### canonifyURLs
|
||||
|
||||
@@ -271,7 +290,10 @@ See [Image Processing Config](/content-management/image-processing/#imaging-conf
|
||||
|
||||
**Default value:** ""
|
||||
|
||||
A language tag as defined by [RFC 5646](https://datatracker.ietf.org/doc/html/rfc5646). The internal [RSS template](https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml) populates its `<language>` element with this value. The value is not used elsewhere.
|
||||
A language tag as defined by [RFC 5646](https://datatracker.ietf.org/doc/html/rfc5646). This value is used to populate:
|
||||
|
||||
- The `<language>` element in the internal [RSS template](https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml)
|
||||
- The `lang` attribute of the `<html>` element in the internal [alias template](https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/alias.html)
|
||||
|
||||
### languages
|
||||
|
||||
@@ -528,6 +550,19 @@ force = false
|
||||
|
||||
{{< new-in "0.76.0" >}} Setting `force=true` will make a redirect even if there is existing content in the path. Note that before Hugo 0.76 `force` was the default behaviour, but this is inline with how Netlify does it.
|
||||
|
||||
## 404 Server Error Page
|
||||
|
||||
{{< new-in "0.103.0" >}}
|
||||
|
||||
Hugo will, by default, render all 404 errors when running `hugo server` with the `404.html` template. Note that if you have already added one or more redirects to your [Server Config](#server-config), you need to add the 404 redirect explicitly, e.g:
|
||||
|
||||
```toml
|
||||
[[redirects]]
|
||||
from = "/**"
|
||||
to = "/404.html"
|
||||
status = 404
|
||||
```
|
||||
|
||||
## Configure Title Case
|
||||
|
||||
Set `titleCaseStyle` to specify the title style used by the [title](/functions/title/) template function and the automatic section titles in Hugo. It defaults to [AP Stylebook](https://www.apstylebook.com/) for title casing, but you can also set it to `Chicago` or `Go` (every word starts with a capital letter).
|
||||
|
||||
@@ -31,6 +31,7 @@ Running the `hugo new site` generator from the command line will create a direct
|
||||
├── content
|
||||
├── data
|
||||
├── layouts
|
||||
├── public
|
||||
├── static
|
||||
└── themes
|
||||
```
|
||||
@@ -45,7 +46,7 @@ The following is a high-level overview of each of the directories with links to
|
||||
By default, Hugo will create new content files with at least `date`, `title` (inferred from the file name), and `draft = true`. This saves time and promotes consistency for sites using multiple content types. You can create your own [archetypes][] with custom preconfigured front matter fields as well.
|
||||
|
||||
[`assets`][]
|
||||
: Stores all the files which need be processed by [Hugo Pipes]({{< ref "/hugo-pipes" >}}). Only the files whose `.Permalink` or `.RelPermalink` are used will be published to the `public` directory. Note: assets directory is not created by default.
|
||||
: Stores all the files which need be processed by [Hugo Pipes](/hugo-pipes/). Only the files whose `.Permalink` or `.RelPermalink` are used will be published to the `public` directory. Note: assets directory is not created by default.
|
||||
|
||||
[`config`](/getting-started/configuration/)
|
||||
: Hugo ships with a large number of [configuration directives][].
|
||||
@@ -71,11 +72,11 @@ used by Hugo when generating your website. You can write these files in YAML, JS
|
||||
From **Hugo 0.31** you can have multiple static directories.
|
||||
{{% /note %}}
|
||||
|
||||
resources
|
||||
[`resources`][]
|
||||
: Caches some files to speed up generation. Can be also used by template authors to distribute built SASS files, so you don't have to have the preprocessor installed. Note: resources directory is not created by default.
|
||||
|
||||
|
||||
[archetypes]: /content-management/archetypes/
|
||||
[`assets`]: /hugo-pipes/introduction#asset-directory/
|
||||
[configuration directives]: /getting-started/configuration/#all-configuration-settings
|
||||
[`content`]: /content-management/organization/
|
||||
[content section]: /content-management/sections/
|
||||
@@ -84,6 +85,7 @@ resources
|
||||
[homepage]: /templates/homepage/
|
||||
[`layouts`]: /templates/
|
||||
[`static`]: /content-management/static-files/
|
||||
[`resources`]: /getting-started/configuration/#configure-file-caches
|
||||
[lists]: /templates/list/
|
||||
[pagevars]: /variables/page/
|
||||
[partials]: /templates/partials/
|
||||
@@ -93,4 +95,3 @@ resources
|
||||
[taxonomies]: /content-management/taxonomies/
|
||||
[taxonomy templates]: /templates/taxonomy-templates/
|
||||
[types]: /content-management/types/
|
||||
[`assets`]: {{< ref "/hugo-pipes/introduction#asset-directory" >}}
|
||||
|
||||
@@ -30,6 +30,12 @@ Hugo in Action is a step-by-step guide to using Hugo to create static websites.
|
||||
|
||||
[Build Websites with Hugo - Fast Web Development with Markdown (2020)](https://pragprog.com/titles/bhhugo/) by Brian P. Hogan.
|
||||
|
||||
## Beginner tutorials
|
||||
|
||||
### Hugo tutorial by CloudCannon
|
||||
|
||||
[Step-by-step written tutorial](https://cloudcannon.com/community/learn/hugo-101/) to teach you the basics of creating a Hugo site.
|
||||
|
||||
## Video tutorials
|
||||
|
||||
### Video Playlist by Mike Dane
|
||||
|
||||
@@ -109,6 +109,7 @@ scoop install hugo-extended
|
||||
#### Prerequisite Tools
|
||||
|
||||
* [Git][installgit]
|
||||
* [GCC][] (For Windows users only)
|
||||
* [Go (at least Go 1.11)](https://golang.org/dl/)
|
||||
|
||||
#### Fetch from GitHub
|
||||
@@ -126,7 +127,14 @@ go install --tags extended
|
||||
Remove `--tags extended` if you do not want/need Sass/SCSS support.
|
||||
|
||||
{{% note %}}
|
||||
If you are a Windows user, substitute the `$HOME` environment variable above with `%USERPROFILE%`.
|
||||
|
||||
##### For installation on Windows
|
||||
|
||||
* Substitute the `$HOME` environment variable above with `%USERPROFILE%`.
|
||||
* If you install `--tags extended` version, you may encounter this error `"gcc": executable file not found in %PATH%`
|
||||
* Please make sure you have installed `gcc` command and add it to `%PATH%`.
|
||||
* "MinGW" is recommended, it has been tested and built successfully
|
||||
|
||||
{{% /note %}}
|
||||
|
||||
## macOS
|
||||
@@ -289,21 +297,21 @@ If `hugo` is not in your `PATH`:
|
||||
|
||||
If your default shell is zsh:
|
||||
|
||||
```
|
||||
nano ~/.zprofile
|
||||
```
|
||||
|
||||
If your default shell is bash:
|
||||
|
||||
```
|
||||
nano ~/.bash_profile
|
||||
```
|
||||
```
|
||||
nano ~/.zprofile
|
||||
```
|
||||
|
||||
If your default shell is bash:
|
||||
|
||||
```
|
||||
nano ~/.bash_profile
|
||||
```
|
||||
|
||||
3. Insert a line to add `$HOME/bin` to your existing `PATH`.
|
||||
|
||||
```
|
||||
export PATH=$PATH:$HOME/bin
|
||||
```
|
||||
```
|
||||
export PATH=$PATH:$HOME/bin
|
||||
```
|
||||
|
||||
4. Save the file by pressing Control-X, then Y.
|
||||
|
||||
@@ -358,7 +366,7 @@ The following aims to be a complete guide to installing Hugo on your Windows PC.
|
||||
|
||||
{{< youtube G7umPCU-8xc >}}
|
||||
|
||||
### Assumptions
|
||||
### Assumptions for Windows
|
||||
|
||||
1. You will use `C:\Hugo\Sites` as the starting point for your new project.
|
||||
2. You will use `C:\Hugo\bin` to store executable files.
|
||||
@@ -376,7 +384,12 @@ You'll need a place to store the Hugo executable, your [content][], and the gene
|
||||
|
||||
1. Download the latest zipped Hugo executable from [Hugo Releases][releases].
|
||||
2. Extract all contents to your `..\Hugo\bin` folder.
|
||||
3. In PowerShell or your preferred CLI, add the `hugo.exe` executable to your PATH by navigating to `C:\Hugo\bin` (or the location of your hugo.exe file) and use the command `set PATH=%PATH%;C:\Hugo\bin`. If the `hugo` command does not work after a reboot, you may have to run the command prompt as administrator.
|
||||
3. Open Windows Command Line (cmd, "DOS") to add the `hugo.exe` executable to your PATH
|
||||
* do `set PATH=%PATH%;C:\Hugo\bin` to have hugo in PATH for the currently opened cmd box
|
||||
* do `setx PATH "%PATH%;C:\Hugo\bin"` to have hugo in PATH for every newly opened cmd box
|
||||
* note: "setx", not "set", plus syntax 'key "val"', not 'key=val'
|
||||
|
||||
> You may also use "Git CMD" from the [Git for Windows package](https://gitforwindows.org/) for the native Windows commands [set](https://ss64.com/nt/set.html) and [setx](https://ss64.com/nt/setx.html), but not "Git Bash", PowerShell, or any other "CLI" with different commands
|
||||
|
||||
### Less-technical Users
|
||||
|
||||
@@ -468,11 +481,15 @@ Directory of C:\hugo\sites\example.com
|
||||
|
||||
In any of the [Linux distributions that support snaps][snaps], you may install the "extended" Sass/SCSS version with this command:
|
||||
|
||||
snap install hugo --channel=extended
|
||||
```
|
||||
snap install hugo --channel=extended
|
||||
```
|
||||
|
||||
To install the non-extended version without Sass/SCSS support:
|
||||
|
||||
snap install hugo
|
||||
```
|
||||
snap install hugo
|
||||
```
|
||||
|
||||
To switch between the two, use either `snap refresh hugo --channel=extended` or `snap refresh hugo --channel=stable`.
|
||||
|
||||
@@ -484,7 +501,9 @@ Hugo installed via Snap can write only inside the user’s `$HOME` directory---a
|
||||
|
||||
[@anthonyfok](https://github.com/anthonyfok) and friends in the [Debian Go Packaging Team](https://go-team.pages.debian.net/) maintains an official hugo [Debian package](https://packages.debian.org/hugo) which is shared with [Ubuntu](https://packages.ubuntu.com/hugo) and is installable via `apt-get`:
|
||||
|
||||
sudo apt-get install hugo
|
||||
```
|
||||
sudo apt-get install hugo
|
||||
```
|
||||
|
||||
What this installs depends on your Debian/Ubuntu version. On Ubuntu bionic (18.04), this installs the non-extended version without Sass/SCSS support. On Ubuntu disco (19.04), this installs the extended version with Sass/SCSS support.
|
||||
|
||||
@@ -495,14 +514,16 @@ This option is not recommended because the Hugo in Linux package managers for De
|
||||
You can also install Hugo from the Arch Linux [community](https://www.archlinux.org/packages/community/x86_64/hugo/) repository. Applies also to derivatives such as Manjaro.
|
||||
|
||||
```
|
||||
sudo pacman -Syu hugo
|
||||
sudo pacman -S hugo
|
||||
```
|
||||
|
||||
### Fedora, Red Hat and CentOS
|
||||
|
||||
Fedora maintains an [official package for Hugo](https://packages.fedoraproject.org/pkgs/hugo/hugo) which may be installed with:
|
||||
|
||||
sudo dnf install hugo
|
||||
```
|
||||
sudo dnf install hugo
|
||||
```
|
||||
|
||||
For the latest version, the Hugo package maintained by [@daftaupe](https://github.com/daftaupe) at Fedora Copr is recommended:
|
||||
|
||||
@@ -530,7 +551,9 @@ sudo eopkg install hugo
|
||||
|
||||
OpenBSD provides a package for Hugo via `pkg_add`:
|
||||
|
||||
doas pkg_add hugo
|
||||
```
|
||||
doas pkg_add hugo
|
||||
```
|
||||
|
||||
|
||||
## Upgrade Hugo
|
||||
@@ -551,6 +574,7 @@ Now that you've installed Hugo, read the [Quick Start guide][quickstart] and exp
|
||||
[dep]: https://github.com/golang/dep
|
||||
[highlight shortcode]: /content-management/shortcodes/#highlight
|
||||
[installgit]: https://git-scm.com/
|
||||
[GCC]: http://www.mingw.org/
|
||||
[installgo]: https://golang.org/dl/
|
||||
[linuxbrew]: https://docs.brew.sh/Homebrew-on-Linux
|
||||
[quickstart]: /getting-started/quick-start/
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: Host on 21YunBox
|
||||
linktitle: Host on 21YunBox
|
||||
description: Host your Hugo site with 21YunBox's blazing fast Chinese CDN, fully-managed SSL and auto deploys from Gitee.
|
||||
date: 2021-01-06
|
||||
publishdate: 2021-01-06
|
||||
lastmod: 2021-01-06
|
||||
categories: [hosting and deployment]
|
||||
keywords: [21yunbox,hosting,deployment]
|
||||
authors: [Toby Glei]
|
||||
menu:
|
||||
docs:
|
||||
parent: "hosting-and-deployment"
|
||||
weight: 10
|
||||
weight: 10
|
||||
sections_weight: 10
|
||||
draft: false
|
||||
aliases: []
|
||||
toc: true
|
||||
---
|
||||
|
||||
[21YunBox](https://www.21yunbox.com) is a fully-managed cloud platform dedicated to make web deployment easy within the Chinese Great Firewall where you can host static sites, backend APIs, databases, cron jobs, and all your other apps in one place. It provides blazing fast Chinese CDN, continuous deployment, one-click HTTPS and [other services like managed databases and backend web services](https://www.21yunbox.com/docs/), providing an avenue to launch web projects in China.
|
||||
|
||||
21YunBox includes the following features:
|
||||
|
||||
- Continuous, automatic builds & deploys from GitHub and Gitee
|
||||
- Automatic SSL certificates through [Let's Encrypt](https://letsencrypt.org)
|
||||
- Instant cache invalidation with a blazing fast, Chinese CDN
|
||||
- Unlimited [custom domains](https://www.21yunbox.com/docs/#/custom-domains)
|
||||
- Automatic [Brotli compression](https://en.wikipedia.org/wiki/Brotli) for faster sites
|
||||
- Native HTTP/2 support
|
||||
- Automatic HTTP → HTTPS redirects
|
||||
- Custom URL redirects and rewrites
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This guide assumes you already have a Hugo project to deploy. If you need a project, use the [Quick Start](/getting-started/quick-start/) to get started or fork 21YunBox's [Hugo Example](https://gitee.com/eryiyunbox-examples/hello-hugo) before continuing.
|
||||
|
||||
## Setup
|
||||
|
||||
You can set up a Hugo site on 21YunBox in two quick steps:
|
||||
|
||||
1. Create a new web service on 21YunBox, and give 21YunBox permission to access your GitHub or Gitee repo.
|
||||
2. Use the following values during creation:
|
||||
|
||||
| Field | Value |
|
||||
| --------------------- | ------------------------------------------------ |
|
||||
| **Environment** | `Static Site` |
|
||||
| **Build Command** | `hugo --gc --minify` (or your own build command) |
|
||||
| **Publish Directory** | `./public` (or your own output directory) |
|
||||
|
||||
That's it! Your site will be live on your 21YunBox URL (which looks like `yoursite.21yunbox.com`) as soon as the build is done.
|
||||
|
||||
## Continuous deploys
|
||||
|
||||
Now that 21YunBox is connected to your repo, it will automatically build and publish your site any time you push to GitHub.
|
||||
|
||||
Every deploy automatically and instantly invalidates the CDN cache, so your users can always access the latest content on your site.
|
||||
|
||||
## Custom domains
|
||||
|
||||
Add your own domains to your site easily using 21YunBox's [custom domains](https://www.21yunbox.com/docs/#/custom-domains) guide.
|
||||
|
||||
## Support
|
||||
Click [here](https://www.21yunbox.com/docs/#/contact) to contact with 21YunBox' experts if you need help.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title: Hosting on Azure Static Web Apps
|
||||
linktitle: Hosting on Azure Static Web Apps
|
||||
description: Learn how to deploy a Hugo application to Azure Static Web Apps.
|
||||
date: 2022-05-09
|
||||
publishdate: 2022-05-09
|
||||
categories: [hosting and deployment]
|
||||
keywords: [hosting,Azure Static Web Apps]
|
||||
authors: [Azure Static Web Apps]
|
||||
menu:
|
||||
docs:
|
||||
parent: "hosting-and-deployment"
|
||||
weight: 200
|
||||
weight: 200
|
||||
sections_weight: 200
|
||||
draft: false
|
||||
toc: true
|
||||
aliases: []
|
||||
---
|
||||
|
||||
You can create and deploy a Hugo web application to Azure Static Web Apps. The final result is a new Azure Static Web App with associated GitHub Actions that give you control over how the app is built and published. You'll learn how to create a Hugo app, setup an Azure Static Web App and deploy the Hugo app to Azure.
|
||||
|
||||
Here's the tutorial on how to [Publish a Hugo site to Azure Static Web Apps](https://docs.microsoft.com/en-us/azure/static-web-apps/publish-hugo).
|
||||
@@ -47,7 +47,7 @@ As mentioned in the [GitHub Pages documentation][ghorgs], you can host a user/or
|
||||
|
||||
## Build Hugo With GitHub Action
|
||||
|
||||
GitHub executes your software development workflows. Everytime you push your code on the Github repository, Github Actions will build the site automatically.
|
||||
GitHub executes your software development workflows. Everytime you push your code on the GitHub repository, Github Actions will build the site automatically.
|
||||
|
||||
Create a file in `.github/workflows/gh-pages.yml` containing the following content (based on [actions-hugo](https://github.com/marketplace/actions/hugo-setup)):
|
||||
|
||||
@@ -62,9 +62,9 @@ on:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true # Fetch Hugo themes (true OR recursive)
|
||||
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
|
||||
@@ -88,10 +88,12 @@ jobs:
|
||||
|
||||
For more advanced settings [actions-hugo](https://github.com/marketplace/actions/hugo-setup) and [actions-gh-pages](https://github.com/marketplace/actions/github-pages-action).
|
||||
|
||||
## Github pages setting
|
||||
## GitHub pages setting
|
||||
|
||||
By default, the GitHub action pushes the generated content to the `gh-pages` branch. This means GitHub has to serve your `gh-pages` branch as a GitHub Pages branch. You can change this setting by going to Settings > GitHub Pages, and change the source branch to `gh-pages`.
|
||||
|
||||
## Change baseURL in config.toml
|
||||
|
||||
Don't forget to rename your `baseURL` in `config.toml` with the value `https://<USERNAME>.github.io` for your user repository or `https://<USERNAME>.github.io/<REPOSITORY_NAME>` for a project repository.
|
||||
|
||||
Unless this is present in your `config.toml`, your website won't work.
|
||||
|
||||
@@ -65,6 +65,10 @@ compact [bool]
|
||||
verbose [bool]
|
||||
: Log everything
|
||||
|
||||
sourceMap [string]
|
||||
: Output `inline` or `external` sourcemap from the babel compile. External sourcemaps will be written to the target with the output file name + ".map". Input sourcemaps can be read from js.Build and node modules and combined into the output sourcemaps.
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
```go-html-template
|
||||
|
||||
@@ -70,6 +70,10 @@ With the above, these imports should work in both scenarios:
|
||||
import * as React from 'react'
|
||||
import * as ReactDOM from 'react-dom';
|
||||
```
|
||||
sourceMap [string, bool] {{< new-in "0.75.0" >}}
|
||||
: Let `js.Build` output sourceMap. Current only inline is supported. true defaults to inline.
|
||||
One of: '`inline`, `external`
|
||||
Default is "" (disabled)
|
||||
|
||||
target [string]
|
||||
: The language target.
|
||||
@@ -93,7 +97,7 @@ format [string] {{< new-in "0.74.3" >}}
|
||||
Default is `iife`, a self-executing function, suitable for inclusion as a <script> tag.
|
||||
|
||||
sourceMap
|
||||
: Whether to generate source maps. Enum, currently only `inline` (we will improve that).
|
||||
: Whether to generate `inline` or `external` sourcemap from esbuild. External sourcemaps will be written to the target with the output file name + ".map". Input sourcemaps can be read from js.Build and node modules and combined into the output sourcemaps.
|
||||
|
||||
### Import JS code from /assets
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ If you are using the Hugo Snap package, PostCSS and plugin(s) need to be install
|
||||
### Options
|
||||
|
||||
config [string]
|
||||
: Path to the PostCSS configuration file
|
||||
: Set a custom directory to look for a config file
|
||||
|
||||
noMap [bool]
|
||||
: Default is `false`. Disable the default inline sourcemaps
|
||||
@@ -63,7 +63,7 @@ syntax [string]
|
||||
: Custom postcss syntax
|
||||
|
||||
```go-html-template
|
||||
{{ $options := dict "config" "customPostCSS.js" "noMap" true }}
|
||||
{{ $options := dict "config" "/path/to/custom-config-directory" "noMap" true }}
|
||||
{{ $style := resources.Get "css/main.css" | resources.PostCSS $options }}
|
||||
|
||||
{{ $options := dict "use" "autoprefixer postcss-color-alpha" }}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
A business page for Flesland Flis AS. A Norwegian Tiler located in Bergen.
|
||||
|
||||
The page is designed and developed by Sindre Gusdal:
|
||||
|
||||
* [Absoluttweb AS](https://www.absoluttweb.no)
|
||||
* [Sindre Gusdal](https://www.linkedin.com/in/sindregusdal/)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 302 KiB |
@@ -1,24 +0,0 @@
|
||||
---
|
||||
|
||||
title: Flesland Flis AS
|
||||
date: 2018-04-24
|
||||
description: "showcase: Business Page for a tile shop in Bergen, Norway"
|
||||
siteURL: https://www.fleslandflis.no
|
||||
byline: "[Sindre Gusdal](https://www.absoluttweb.no), Absoluttweb AS"
|
||||
|
||||
---
|
||||
|
||||
For **Flesland Flis** I use a combination of **Hugo, Forestry.io and Netlify**. Static Site Generators and Hugo has been on my radar for a long time, and with all the nice features released in Hugo the last years, it's now my preferred solution for new clients. Also a huge thanks to the guys at [Forestry.io](https://forestry.io), for making such a smooth CMS for Hugo.
|
||||
|
||||
The #1 reason why I love Hugo is the logic between content and layout, and of course the speed. Compared to solutions like Jekyll, Hugo is just better at all the stuff I value the most - speed, flexibility, theming and more.
|
||||
|
||||
### Thanks, Hugo!
|
||||
|
||||
Today I use Hugo in a combination with GULP and Foundation 6 + my own Hugo starter theme. This works great for me, and gives me all the flexibility I need. Then I can include FancyBox, Responsive Text and other Node Modules when needed.
|
||||
|
||||
In the past I had to do a lot of changes to layout, content and css, if the client f.ex needed an extra PDF or an image-gallery to a certain page. Just small details not fitting in the template, would be a hassle. So updating existing webpages was boring and time consuming.
|
||||
|
||||
Today I just copy-paste a new layout file, adds some frontmatter, Pushes to GIT and that special page is done.
|
||||
|
||||
**Gotta love it:)**
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
The site is built by:
|
||||
|
||||
* [Lauren Waller](https://twitter.com/waller_texas)
|
||||
* [Wayne Ashley Berry](https://twitter.com/waynethebrain)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 190 KiB |
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Over
|
||||
date: 2018-09-12
|
||||
description: "Showcase: \"People from all disciplines contribute to our website; Hugo’s single static binary makes that possible.\""
|
||||
siteURL: https://madewithover.com/
|
||||
|
||||
---
|
||||
|
||||
At Over we're into creativity, and technology should not get in the way. We want it to be easy for everyone to create, and Hugo does the same for us. That's one of the reasons many of us are fond of using it.
|
||||
|
||||
People from all disciplines contribute to our website, be it legal documentation, layout and design, recruiting, marketing and of course… engineering. Hugo allows us to do this with as little friction as possible. A lot of this comes down to Hugo being distributed as a single static binary. Copy, paste, run... and you're up and running!
|
||||
|
||||
We use Wercker for continuous integration and deployments, [GitHub](https://github.com/) for contributing to and writing markdown and [Firebase](https://firebase.google.com/docs/hosting/) for hosting.
|
||||
|
||||
This infrastructure takes all the pressure off our engineers, anyone can contribute to our website. Anyone else can review the changes, and of course anyone with permission can deploy those approved changes as well!
|
||||
|
||||
We're busy working on a few new features for our website, and Hugo continues to deliver above and beyond. We're so happy with the choice we made to use Hugo and to us it has become the de-facto static site generator.
|
||||
@@ -4,7 +4,6 @@ linktitle: 404 Page
|
||||
description: If you know how to create a single page template, you have unlimited options for creating a custom 404.
|
||||
date: 2017-02-01
|
||||
publishdate: 2017-02-01
|
||||
lastmod: 2017-03-31
|
||||
categories: [templates]
|
||||
keywords: [404, page not found]
|
||||
menu:
|
||||
@@ -36,7 +35,7 @@ This is a basic example of a 404.html template:
|
||||
{{ define "main"}}
|
||||
<main id="main">
|
||||
<div>
|
||||
<h1 id="title"><a href="{{ "/" | relURL }}">Go Home</a></h1>
|
||||
<h1 id="title"><a href="{{ "" | relURL }}">Go Home</a></h1>
|
||||
</div>
|
||||
</main>
|
||||
{{ end }}
|
||||
@@ -54,7 +53,8 @@ Your 404.html file can be set to load automatically when a visitor enters a mist
|
||||
* Caddy Server. Use the `handle_errors` directive to specify error pages for one or more status codes. [Details here](https://caddyserver.com/docs/caddyfile/directives/handle_errors)
|
||||
* Netlify. Add `/* /404.html 404` to `content/_redirects`. [Details Here](https://www.netlify.com/docs/redirects/#custom-404)
|
||||
* Azure Static website. You can specify the `Error document path` in the Static website configuration page of the Azure portal. [More details are available in the Static website documentation](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website).
|
||||
* DigitalOcean App Platform. You can specify `error_document` in your app specification file or use control panel to set up error document. [Details here](https://docs.digitalocean.com/products/app-platform/references/app-specification-reference/).
|
||||
* DigitalOcean App Platform. You can specify `error_document` in your app specification file or use control panel to set up error document. [Details here](https://docs.digitalocean.com/products/app-platform/how-to/manage-static-sites/#configure-a-static-site).
|
||||
* [Firebase Hosting](https://firebase.google.com/docs/hosting/full-config#404): `/404.html` automatically gets used as the 404 page.
|
||||
|
||||
{{% note %}}
|
||||
`hugo server` will not automatically load your custom `404.html` file, but you
|
||||
|
||||
@@ -76,9 +76,11 @@ To enable this menu, configure `sectionPagesMenu` in your site `config`:
|
||||
sectionPagesMenu = "main"
|
||||
```
|
||||
|
||||
The menu name can be anything, but take a note of what it is.
|
||||
The menu name can be anything, but take a note of what it is.
|
||||
|
||||
This will create a menu with all the sections as menu items and all the sections' pages as "shadow-members". The _shadow_ implies that the pages isn't represented by a menu-item themselves, but this enables you to create a top-level menu like this:
|
||||
This will create a menu with all the sections as menu items and all the sections' pages as "shadow-members". Ensure that all first level directories that you would like to show up on this menu are [Branch Bundles](https://gohugo.io/content-management/sections/). Leaf Bundles do not form sections.
|
||||
|
||||
The _shadow_ implies that the pages isn't represented by a menu-item themselves, but this enables you to create a top-level menu like this:
|
||||
|
||||
```
|
||||
<nav class="sidebar-nav">
|
||||
@@ -179,4 +181,4 @@ Here's an example:
|
||||
|
||||
{{% note %}}
|
||||
With Menu-level .Params they can easily exist on one menu item but not another. It's recommended to access them gracefully using the [with function](/functions/with).
|
||||
{{% /note %}}
|
||||
{{% /note %}}
|
||||
|
||||
@@ -303,7 +303,7 @@ The rendered output of the HTML example code block will be as follows:
|
||||
|
||||
### Nested Shortcode: Image Gallery
|
||||
|
||||
Hugo's [`.Parent` shortcode variable][parent] returns a boolean value depending on whether the shortcode in question is called within the context of a *parent* shortcode. This provides an inheritance model for common shortcode parameters.
|
||||
Hugo's [`.Parent` shortcode variable][parent] provides access to the parent shortcode context when the shortcode in question is called within the context of a *parent* shortcode. This provides an inheritance model for common shortcode parameters.
|
||||
|
||||
The following example is contrived but demonstrates the concept. Assume you have a `gallery` shortcode that expects one named `class` parameter:
|
||||
|
||||
|
||||
@@ -28,4 +28,5 @@ toc: false
|
||||
|
||||
* [DATOCMS](https://www.datocms.com) DatoCMS is a fully customizable administrative area for your static websites. Use your favorite website generator, let your clients publish new content independently, and the host the site anywhere you like.
|
||||
* [Forestry.io](https://forestry.io/). Forestry is a git-backed CMS for Hugo, Gatsby, Jekyll and VuePress websites with support for GitHub, GitLab, Bitbucket and Azure Devops. Forestry provides a nice user interface to edit and model content for non technical editors. It supports S3, Cloudinary and Netlify Large Media integrations for storing media. Every time an update is made via the CMS, Forestry will commit changes back to your repo and vice-versa.
|
||||
* [CloudCannon](https://cloudcannon.com/hugo-cms/). The intuitive Git-based CMS for your Hugo website. CloudCannon syncs changes from your Git repository and pushes content changes back, so your development and content teams are always in sync. Edit all of your content on the page with visual editing, build entire pages with reusable custom components and then publish confidently.
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ toc: true
|
||||
A static website with a dynamic search function? Yes, Hugo provides an alternative to embeddable scripts from Google or other search engines for static websites. Hugo allows you to provide your visitors with a custom search function by indexing your content files directly.
|
||||
|
||||
* [GitHub Gist for Hugo Workflow](https://gist.github.com/sebz/efddfc8fdcb6b480f567). This gist contains a simple workflow to create a search index for your static website. It uses a simple Grunt script to index all your content files and [lunr.js](https://lunrjs.com/) to serve the search results.
|
||||
* [hugo-elasticsearch](https://www.npmjs.com/package/hugo-elasticsearch). Generate [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) indexes for Hugo static sites by parsing front matter. Hugo-Elasticsearch will generate a newline delimited JSON (NDJSON) file that can be bulk uploaded into Elasticsearch using any one of the available [clients](https://www.elastic.co/guide/en/elasticsearch/client/index.html).
|
||||
|
||||
* [hugo-lunr](https://www.npmjs.com/package/hugo-lunr). A simple way to add site search to your static Hugo site using [lunr.js](https://lunrjs.com/). Hugo-lunr will create an index file of any html and markdown documents in your Hugo project.
|
||||
* [hugo-lunr-zh](https://www.npmjs.com/package/hugo-lunr-zh). A bit like Hugo-lunr, but Hugo-lunr-zh can help you separate the Chinese keywords.
|
||||
* [Github Gist for Fuse.js integration](https://gist.github.com/eddiewebb/735feb48f50f0ddd65ae5606a1cb41ae). This gist demonstrates how to leverage Hugo's existing build time processing to generate a searchable JSON index used by [Fuse.js](https://fusejs.io/) on the client side. Although this gist uses Fuse.js for fuzzy matching, any client side search tool capable of reading JSON indexes will work. Does not require npm, grunt or other build-time tools except Hugo!
|
||||
|
||||
@@ -23,11 +23,12 @@ Know of a Hugo-related starter kit that isn't mentioned here? [Please add it to
|
||||
The following starter kits are developed by active members of the Hugo community. If you find yourself having issues with any of the projects, it's best to file an issue directly with the project's maintainer(s).
|
||||
{{% /note %}}
|
||||
|
||||
* [Wowchemy][]. Wowchemy is the 5,500+ star open source Hugo starter kit and website builder trusted by 750,000+ sites since 2016. Create _any_ kind of site with [50+ templates, widgets, and extensions](https://wowchemy.com/). Translated into 35+ languages and backed by a large, active community of 150+ contributors.
|
||||
* [Hugo Wrapper][hugow]. Hugo Wrapper is a POSIX-style shell script which acts as a wrapper to download and run Hugo binary for your platform. It can be executed in variety of [Operating Systems][hugow-test] and [Command Shells][hugow-test].
|
||||
* [GOHUGO AMP][]. GoHugo AMP is a starter theme that aims to make it easy to adopt [Google's AMP Project][amp]. The starter kit comes with 40+ shortcodes and partials plus automatic structured data. The project also includes a [separate site with extensive documentation][gohugodocs].
|
||||
* [Hyas][]. Hyas is a Hugo starter helping you build modern websites that are secure, fast, and SEO-ready — by default. It is Netlify-ready (functions, redirects, headers) and comes with [documentation](https://gethyas.com/) to easily make it your own.
|
||||
|
||||
|
||||
[Wowchemy]: https://github.com/wowchemy/wowchemy-hugo-modules
|
||||
[addkit]: https://github.com/gohugoio/hugo/edit/master/docs/content/en/tools/starter-kits.md
|
||||
[amp]: https://amp.dev
|
||||
[GOHUGO AMP]: https://github.com/wildhaber/gohugo-amp
|
||||
|
||||
@@ -4,7 +4,6 @@ linktitle: Build Performance
|
||||
description: An overview of features used for diagnosing and improving performance issues in site builds.
|
||||
date: 2017-03-12
|
||||
publishdate: 2017-03-12
|
||||
lastmod: 2017-03-12
|
||||
keywords: [performance, build]
|
||||
categories: [troubleshooting]
|
||||
menu:
|
||||
@@ -16,10 +15,6 @@ aliases: []
|
||||
toc: true
|
||||
---
|
||||
|
||||
{{% note %}}
|
||||
The example site used below is from https://github.com/gohugoio/hugo/tree/master/examples/blog
|
||||
{{% /note %}}
|
||||
|
||||
## Template Metrics
|
||||
|
||||
Hugo is a very fast static site generator, but it is possible to write
|
||||
|
||||
@@ -21,6 +21,8 @@ aliases: [/faq/]
|
||||
|
||||
Is your markdown file [in draft mode](https://gohugo.io/content-management/front-matter/#front-matter-variables)? When testing, run `hugo server` with the `-D` or `--buildDrafts` [switch](https://gohugo.io/getting-started/usage/#draft-future-and-expired-content).
|
||||
|
||||
Is your markdown file part of a [leaf bundle](/content-management/page-bundles/)? If there is an `index.md` file in the same or any parent directory then other markdown files will not be rendered as individual pages.
|
||||
|
||||
## Can I set configuration variables via OS environment?
|
||||
|
||||
Yes you can! See [Configure with Environment Variables](/getting-started/configuration/#configure-with-environment-variables).
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ module github.com/gohugoio/hugoDocs
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/gohugoio/gohugoioTheme v0.0.0-20220228085601-7cfbda06d135 // indirect
|
||||
require github.com/gohugoio/gohugoioTheme v0.0.0-20220912070954-88dcaf003b4d // indirect
|
||||
|
||||
@@ -33,3 +33,7 @@ github.com/gohugoio/gohugoioTheme v0.0.0-20211211125852-b85e21c1f3d6 h1:lAgdWrn8
|
||||
github.com/gohugoio/gohugoioTheme v0.0.0-20211211125852-b85e21c1f3d6/go.mod h1:kpw3SS48xZvLQGEXKu8u5XHgXkPvL8DX3oGa07+z8Bs=
|
||||
github.com/gohugoio/gohugoioTheme v0.0.0-20220228085601-7cfbda06d135 h1:6hVzfE9YhSsZP5t6jWjvVp7MoPm7Y5fEhH/ls4ahhKk=
|
||||
github.com/gohugoio/gohugoioTheme v0.0.0-20220228085601-7cfbda06d135/go.mod h1:kpw3SS48xZvLQGEXKu8u5XHgXkPvL8DX3oGa07+z8Bs=
|
||||
github.com/gohugoio/gohugoioTheme v0.0.0-20220905093719-cb8b64046950 h1:Ovlh3nuy/aNptYZHmIra2MP+ZUEqiihY0HxvhdaMqGg=
|
||||
github.com/gohugoio/gohugoioTheme v0.0.0-20220905093719-cb8b64046950/go.mod h1:kpw3SS48xZvLQGEXKu8u5XHgXkPvL8DX3oGa07+z8Bs=
|
||||
github.com/gohugoio/gohugoioTheme v0.0.0-20220912070954-88dcaf003b4d h1:UYJL6RmEepprvlgHvDnFCWtPOvjmqzFCQ90cRDRBO7U=
|
||||
github.com/gohugoio/gohugoioTheme v0.0.0-20220912070954-88dcaf003b4d/go.mod h1:kpw3SS48xZvLQGEXKu8u5XHgXkPvL8DX3oGa07+z8Bs=
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
project = "hugoDocs"
|
||||
|
||||
[release_settings]
|
||||
name = "${HUGORELEASER_TAG}"
|
||||
type = "github"
|
||||
repository = "hugoDocs"
|
||||
repository_owner = "gohugoio"
|
||||
|
||||
draft = true
|
||||
prerelease = false
|
||||
|
||||
[release_settings.release_notes_settings]
|
||||
generate = true
|
||||
generate_on_host = false
|
||||
short_threshold = 10
|
||||
short_title = "What's Changed"
|
||||
|
||||
groups = [
|
||||
{ regexp = "snapcraft:|Merge commit|Merge branch|netlify:|release:|Squashed", ignore = true },
|
||||
{ title = "Typo fixes", regexp = "typo", ordinal = 20 },
|
||||
{ title = "Dependency Updates", regexp = "deps", ordinal = 30 },
|
||||
{ title = "Improvements", regexp = ".*", ordinal = 10 },
|
||||
]
|
||||
|
||||
[[releases]]
|
||||
paths = ["archives/**"]
|
||||
# In this file we have only one release, but path can be used to partition the release step, e.g.:
|
||||
# hugoreleaser release -paths "releases/myrelease"
|
||||
path = "myrelease"
|
||||
+4
-4
@@ -3,7 +3,7 @@ publish = "public"
|
||||
command = "hugo --gc --minify"
|
||||
|
||||
[context.production.environment]
|
||||
HUGO_VERSION = "0.101.0"
|
||||
HUGO_VERSION = "0.102.3"
|
||||
HUGO_ENV = "production"
|
||||
HUGO_ENABLEGITINFO = "true"
|
||||
|
||||
@@ -11,20 +11,20 @@ HUGO_ENABLEGITINFO = "true"
|
||||
command = "hugo --gc --minify --enableGitInfo"
|
||||
|
||||
[context.split1.environment]
|
||||
HUGO_VERSION = "0.101.0"
|
||||
HUGO_VERSION = "0.102.3"
|
||||
HUGO_ENV = "production"
|
||||
|
||||
[context.deploy-preview]
|
||||
command = "hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL"
|
||||
|
||||
[context.deploy-preview.environment]
|
||||
HUGO_VERSION = "0.101.0"
|
||||
HUGO_VERSION = "0.102.3"
|
||||
|
||||
[context.branch-deploy]
|
||||
command = "hugo --gc --minify -b $DEPLOY_PRIME_URL"
|
||||
|
||||
[context.branch-deploy.environment]
|
||||
HUGO_VERSION = "0.101.0"
|
||||
HUGO_VERSION = "0.102.3"
|
||||
|
||||
[context.next.environment]
|
||||
HUGO_ENABLEGITINFO = "true"
|
||||
|
||||
@@ -19,13 +19,13 @@ require (
|
||||
github.com/cli/safeexec v1.0.0
|
||||
github.com/disintegration/gift v1.2.1
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/evanw/esbuild v0.14.43
|
||||
github.com/evanw/esbuild v0.15.7
|
||||
github.com/fortytw2/leaktest v1.3.0
|
||||
github.com/frankban/quicktest v1.14.3
|
||||
github.com/fsnotify/fsnotify v1.5.4
|
||||
github.com/getkin/kin-openapi v0.97.0
|
||||
github.com/getkin/kin-openapi v0.100.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gobuffalo/flect v0.2.5
|
||||
github.com/gobuffalo/flect v0.3.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20210430103248-4c28c89f8013
|
||||
github.com/gohugoio/locales v0.14.0
|
||||
@@ -36,28 +36,28 @@ require (
|
||||
github.com/hairyhenderson/go-codeowners v0.2.3-0.20201026200250-cdc7c0759690
|
||||
github.com/jdkato/prose v1.2.1
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/kyokomi/emoji/v2 v2.2.9
|
||||
github.com/kyokomi/emoji/v2 v2.2.10
|
||||
github.com/magefile/mage v1.13.0
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/mattn/go-isatty v0.0.16
|
||||
github.com/mitchellh/hashstructure v1.1.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/muesli/smartcrop v0.3.0
|
||||
github.com/niklasfasching/go-org v1.6.5
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/pelletier/go-toml/v2 v2.0.2
|
||||
github.com/rogpeppe/go-internal v1.8.1
|
||||
github.com/pelletier/go-toml/v2 v2.0.4
|
||||
github.com/rogpeppe/go-internal v1.9.0
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
||||
github.com/sanity-io/litter v1.5.5
|
||||
github.com/spf13/afero v1.8.2
|
||||
github.com/spf13/afero v1.9.2
|
||||
github.com/spf13/cast v1.5.0
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/spf13/fsync v0.9.0
|
||||
github.com/spf13/jwalterweatherman v1.1.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/tdewolff/minify/v2 v2.11.10
|
||||
github.com/tdewolff/parse/v2 v2.6.0
|
||||
github.com/yuin/goldmark v1.4.12
|
||||
go.uber.org/atomic v1.9.0
|
||||
github.com/tdewolff/minify/v2 v2.12.1
|
||||
github.com/tdewolff/parse/v2 v2.6.3
|
||||
github.com/yuin/goldmark v1.4.14
|
||||
go.uber.org/atomic v1.10.0
|
||||
gocloud.dev v0.24.0
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
|
||||
@@ -91,7 +91,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect
|
||||
github.com/aws/smithy-go v1.8.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.5 // indirect
|
||||
@@ -115,7 +115,7 @@ require (
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 // indirect
|
||||
|
||||
@@ -131,8 +131,6 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/alecthomas/chroma/v2 v2.1.0 h1:ZG9L5/RsxO/xIONrBy8Cgo+5si3d9x3osweXc4VHl0o=
|
||||
github.com/alecthomas/chroma/v2 v2.1.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY=
|
||||
@@ -209,8 +207,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -239,8 +237,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanw/esbuild v0.14.43 h1:4WIYkAf2fLmru3KYtKUB6mdBl4dgpoVcq7hqXhXGVG0=
|
||||
github.com/evanw/esbuild v0.14.43/go.mod h1:GG+zjdi59yh3ehDn4ZWfPcATxjPDUH53iU4ZJbp7dkY=
|
||||
github.com/evanw/esbuild v0.15.7 h1:1oi9ehtc3BTFVXAAsljCc1OQsoMHt4SXipVwaKgmz9s=
|
||||
github.com/evanw/esbuild v0.15.7/go.mod h1:iINY06rn799hi48UqEnaQvVfZWe6W9bET78LbvN8VWk=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
@@ -253,8 +251,8 @@ github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUork
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/getkin/kin-openapi v0.97.0 h1:bsvXZeuGiCW43ZKy6xOY5qfT5fCRYmnJwierblSrHCU=
|
||||
github.com/getkin/kin-openapi v0.97.0/go.mod h1:w4lRPHiyOdwGbOkLIyk+P0qCwlu7TXPCHD/64nSXzgE=
|
||||
github.com/getkin/kin-openapi v0.100.0 h1:8L9xNFNJFDqIRjZwwFjWhTTmTAxPRn/BVTzPn+hOA2s=
|
||||
github.com/getkin/kin-openapi v0.100.0/go.mod h1:w4lRPHiyOdwGbOkLIyk+P0qCwlu7TXPCHD/64nSXzgE=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
@@ -272,8 +270,8 @@ github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTM
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gobuffalo/flect v0.2.5 h1:H6vvsv2an0lalEaCDRThvtBfmg44W/QHXBCYUXf/6S4=
|
||||
github.com/gobuffalo/flect v0.2.5/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8=
|
||||
github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk=
|
||||
github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
@@ -430,8 +428,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/kyokomi/emoji/v2 v2.2.9 h1:UWYkjplPZ4rMPvLxc+/e12/xTqoRcn55oUySkpZ554g=
|
||||
github.com/kyokomi/emoji/v2 v2.2.9/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/kyokomi/emoji/v2 v2.2.10 h1:1z5eMVcxFifsmEoNpdeq4UahbcicgQ4FEHuzrCVwmiI=
|
||||
github.com/kyokomi/emoji/v2 v2.2.10/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magefile/mage v1.13.0 h1:XtLJl8bcCM7EFoO8FyH8XK3t7G5hQAeK+i4tq+veT9M=
|
||||
@@ -443,8 +441,8 @@ github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O
|
||||
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
|
||||
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@@ -467,9 +465,8 @@ github.com/niklasfasching/go-org v1.6.5 h1:5YAIqNTdl6lAOb7lD2AyQ1RuFGPVrAKvUexph
|
||||
github.com/niklasfasching/go-org v1.6.5/go.mod h1:ybv0eGDnxylFUfFE+ySaQc734j/L3+/ChKZ/h63a2wM=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
|
||||
github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pelletier/go-toml/v2 v2.0.4 h1:MHHO+ZUPwPZQ6BmnnT81iQg5cuurp78CRH7rNsguSMk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.4/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -481,8 +478,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
||||
@@ -492,12 +489,12 @@ github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF
|
||||
github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
|
||||
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
|
||||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/spf13/fsync v0.9.0 h1:f9CEt3DOB2mnHxZaftmEOFWjABEvKM/xpf3cUwJrGOY=
|
||||
github.com/spf13/fsync v0.9.0/go.mod h1:fNtJEfG3HiltN3y4cPOz6MLjos9+2pIEqLIgszqhp/0=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
@@ -506,6 +503,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -513,14 +511,15 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/tdewolff/minify/v2 v2.11.10 h1:2tk9nuKfc8YOTD8glZ7JF/VtE8W5HOgmepWdjcPtRro=
|
||||
github.com/tdewolff/minify/v2 v2.11.10/go.mod h1:dHOS3dk+nJ0M3q3uM3VlNzTb70cou+ov0ki7C4PAFgM=
|
||||
github.com/tdewolff/parse/v2 v2.6.0 h1:f2D7w32JtqjCv6SczWkfwK+m15et42qEtDnZXHoNY70=
|
||||
github.com/tdewolff/parse/v2 v2.6.0/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
|
||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/tdewolff/minify/v2 v2.12.1 h1:zcjJTcO0uI+asdT+nd4TjXi3KUmVV/G2kxOKKrgKlGw=
|
||||
github.com/tdewolff/minify/v2 v2.12.1/go.mod h1:p5pwbvNs1ghbFED/ZW1towGsnnWwzvM8iz8l0eURi9g=
|
||||
github.com/tdewolff/parse/v2 v2.6.3 h1:O5rshbkaRmpRtD7k2lG65bEJpcfUMNg5Cx2uRKWVsI8=
|
||||
github.com/tdewolff/parse/v2 v2.6.3/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
|
||||
github.com/tdewolff/test v1.0.7 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM=
|
||||
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -528,8 +527,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0=
|
||||
github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.14 h1:jwww1XQfhJN7Zm+/a1ZA/3WUiEBEroYFNTiV3dKwM8U=
|
||||
github.com/yuin/goldmark v1.4.14/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
@@ -542,8 +541,9 @@ go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
@@ -746,8 +746,9 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
-197
@@ -1,197 +0,0 @@
|
||||
project_name: hugo
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- GOPROXY=https://proxy.golang.org
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
builds:
|
||||
-
|
||||
binary: hugo
|
||||
id: hugo
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
goarm:
|
||||
- 7
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
-
|
||||
binary: hugo
|
||||
id: hugo_unix
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
goos:
|
||||
- freebsd
|
||||
- netbsd
|
||||
- openbsd
|
||||
- dragonfly
|
||||
goarch:
|
||||
- amd64
|
||||
-
|
||||
binary: hugo
|
||||
id: hugo_extended_windows
|
||||
ldflags:
|
||||
- -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
- "-extldflags '-static'"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=x86_64-w64-mingw32-gcc
|
||||
- CXX=x86_64-w64-mingw32-g++
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
- -tags
|
||||
- extended
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- binary: hugo
|
||||
id: hugo_extended_darwin
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=o64-clang
|
||||
- CXX=o64-clang++
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
- -tags
|
||||
- extended
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- binary: hugo
|
||||
id: hugo_extended_linux
|
||||
ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
flags:
|
||||
- -buildmode
|
||||
- exe
|
||||
- -tags
|
||||
- extended
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
hooks:
|
||||
post: ./goreleaser-hook-post-linux.sh
|
||||
release:
|
||||
draft: true
|
||||
|
||||
archives:
|
||||
-
|
||||
id: "hugo"
|
||||
builds: ['hugo', 'hugo_unix']
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
-
|
||||
id: "hugo_extended"
|
||||
builds: ['hugo_extended_windows', 'hugo_extended_linux', 'hugo_extended_darwin']
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
name_template: "{{.ProjectName}}_extended_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
files:
|
||||
- README.md
|
||||
- LICENSE
|
||||
|
||||
nfpms:
|
||||
-
|
||||
id: "hugo"
|
||||
builds: ['hugo']
|
||||
formats:
|
||||
- deb
|
||||
vendor: "gohugo.io"
|
||||
homepage: "https://gohugo.io/"
|
||||
maintainer: "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description: "A Fast and Flexible Static Site Generator built with love in GoLang."
|
||||
license: "Apache-2.0"
|
||||
file_name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
-
|
||||
id: "hugo_extended"
|
||||
builds: ['hugo_extended_linux']
|
||||
formats:
|
||||
- deb
|
||||
vendor: "gohugo.io"
|
||||
homepage: "https://gohugo.io/"
|
||||
maintainer: "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description: "A Fast and Flexible Static Site Generator built with love in GoLang."
|
||||
license: "Apache-2.0"
|
||||
file_name_template: "{{.ProjectName}}_extended_{{.Version}}_{{.Os}}-{{.Arch}}"
|
||||
replacements:
|
||||
amd64: 64bit
|
||||
386: 32bit
|
||||
arm: ARM
|
||||
arm64: ARM64
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
openbsd: OpenBSD
|
||||
netbsd: NetBSD
|
||||
freebsd: FreeBSD
|
||||
dragonfly: DragonFlyBSD
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2022 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hugofs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hugio"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
var (
|
||||
_ afero.Fs = (*hasBytesFs)(nil)
|
||||
_ FilesystemUnwrapper = (*hasBytesFs)(nil)
|
||||
)
|
||||
|
||||
type hasBytesFs struct {
|
||||
afero.Fs
|
||||
hasBytesCallback func(name string, match bool)
|
||||
pattern []byte
|
||||
}
|
||||
|
||||
func NewHasBytesReceiver(delegate afero.Fs, hasBytesCallback func(name string, match bool), pattern []byte) afero.Fs {
|
||||
return &hasBytesFs{Fs: delegate, hasBytesCallback: hasBytesCallback, pattern: pattern}
|
||||
}
|
||||
|
||||
func (fs *hasBytesFs) UnwrapFilesystem() afero.Fs {
|
||||
return fs.Fs
|
||||
}
|
||||
|
||||
func (fs *hasBytesFs) Create(name string) (afero.File, error) {
|
||||
f, err := fs.Fs.Create(name)
|
||||
if err == nil {
|
||||
f = fs.wrapFile(f)
|
||||
}
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (fs *hasBytesFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
|
||||
f, err := fs.Fs.OpenFile(name, flag, perm)
|
||||
if err == nil && isWrite(flag) {
|
||||
f = fs.wrapFile(f)
|
||||
}
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (fs *hasBytesFs) wrapFile(f afero.File) afero.File {
|
||||
return &hasBytesFile{
|
||||
File: f,
|
||||
hbw: &hugio.HasBytesWriter{
|
||||
Pattern: fs.pattern,
|
||||
},
|
||||
hasBytesCallback: fs.hasBytesCallback,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (fs *hasBytesFs) Name() string {
|
||||
return "hasBytesFs"
|
||||
}
|
||||
|
||||
type hasBytesFile struct {
|
||||
hasBytesCallback func(name string, match bool)
|
||||
hbw *hugio.HasBytesWriter
|
||||
afero.File
|
||||
}
|
||||
|
||||
func (h *hasBytesFile) Write(p []byte) (n int, err error) {
|
||||
n, err = h.File.Write(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return h.hbw.Write(p)
|
||||
}
|
||||
|
||||
func (h *hasBytesFile) Close() error {
|
||||
h.hasBytesCallback(h.Name(), h.hbw.Match)
|
||||
return h.File.Close()
|
||||
}
|
||||
@@ -110,7 +110,7 @@ func (f ContentFactory) SectionFromFilename(filename string) (string, error) {
|
||||
|
||||
// CreateContentPlaceHolder creates a content placeholder file inside the
|
||||
// best matching content directory.
|
||||
func (f ContentFactory) CreateContentPlaceHolder(filename string) (string, error) {
|
||||
func (f ContentFactory) CreateContentPlaceHolder(filename string, force bool) (string, error) {
|
||||
filename = filepath.Clean(filename)
|
||||
_, abs, err := f.h.AbsProjectContentDir(filename)
|
||||
|
||||
@@ -130,6 +130,9 @@ _build:
|
||||
|
||||
`
|
||||
|
||||
if force {
|
||||
return abs, afero.WriteReader(f.h.Fs.Source, abs, strings.NewReader(placeholder))
|
||||
}
|
||||
return abs, afero.SafeWriteReader(f.h.Fs.Source, abs, strings.NewReader(placeholder))
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ Hello World.
|
||||
`)
|
||||
b.CreateSites()
|
||||
cf := NewContentFactory(b.H)
|
||||
abs, err := cf.CreateContentPlaceHolder(filepath.FromSlash("mcontent/en/blog/mypage.md"))
|
||||
abs, err := cf.CreateContentPlaceHolder(filepath.FromSlash("mcontent/en/blog/mypage.md"), false)
|
||||
b.Assert(err, qt.IsNil)
|
||||
b.Assert(abs, qt.Equals, filepath.FromSlash("/my/work/mcontent/en/blog/mypage.md"))
|
||||
b.Build(BuildCfg{SkipRender: true})
|
||||
@@ -69,7 +69,7 @@ theme = 'ipsum'
|
||||
b.WithSourceFile(filepath.Join(themeDir, "content/posts/foo.txt"), `Hello.`)
|
||||
b.CreateSites()
|
||||
cf := NewContentFactory(b.H)
|
||||
abs, err := cf.CreateContentPlaceHolder(filepath.FromSlash("posts/test.md"))
|
||||
abs, err := cf.CreateContentPlaceHolder(filepath.FromSlash("posts/test.md"), false)
|
||||
b.Assert(err, qt.IsNil)
|
||||
b.Assert(abs, qt.Equals, filepath.FromSlash("content/posts/test.md"))
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/trace"
|
||||
"strings"
|
||||
@@ -439,23 +438,16 @@ func (h *HugoSites) postProcess() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = afero.Walk(h.BaseFs.PublishFs, "", func(path string, info os.FileInfo, err error) error {
|
||||
if info == nil || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(path, "html") {
|
||||
return nil
|
||||
}
|
||||
|
||||
filenames := helpers.UniqueStrings(h.Deps.FilenameHasPostProcessPrefix)
|
||||
for _, filename := range filenames {
|
||||
filename := filename
|
||||
g.Run(func() error {
|
||||
return handleFile(path)
|
||||
return handleFile(filename)
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Prepare for a new build.
|
||||
h.Deps.FilenameHasPostProcessPrefix = nil
|
||||
for _, s := range h.Sites {
|
||||
s.ResourceSpec.PostProcessResources = make(map[string]postpub.PostPublishedResource)
|
||||
}
|
||||
|
||||
+1
-3
@@ -39,8 +39,6 @@ import (
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/parser/pageparser"
|
||||
|
||||
"github.com/gohugoio/hugo/output"
|
||||
@@ -762,7 +760,7 @@ Loop:
|
||||
case it.IsEOF():
|
||||
break Loop
|
||||
case it.IsError():
|
||||
err := fail(errors.New(it.ValStr(result.Input())), it)
|
||||
err := fail(it.Err, it)
|
||||
currShortcode.err = err
|
||||
return err
|
||||
|
||||
|
||||
@@ -168,6 +168,11 @@ HELLO: {{ $hello.RelPermalink }}
|
||||
HELLO: {{ $hello.RelPermalink }}|Integrity: {{ $hello.Data.Integrity }}|MediaType: {{ $hello.MediaType.Type }}
|
||||
HELLO2: Name: {{ $hello.Name }}|Content: {{ $hello.Content }}|Title: {{ $hello.Title }}|ResourceType: {{ $hello.ResourceType }}
|
||||
|
||||
// Issue #10269
|
||||
{{ $m := dict "relPermalink" $hello.RelPermalink "integrity" $hello.Data.Integrity "mediaType" $hello.MediaType.Type }}
|
||||
{{ $json := jsonify (dict "indent" " ") $m | resources.FromString "hello.json" -}}
|
||||
JSON: {{ $json.RelPermalink }}
|
||||
|
||||
// Issue #8884
|
||||
<a href="hugo.rocks">foo</a>
|
||||
<a href="{{ $hello.RelPermalink }}" integrity="{{ $hello.Data.Integrity}}">Hello</a>
|
||||
@@ -188,6 +193,11 @@ End.`)
|
||||
|
||||
b.AssertFileContent("public/page1/index.html", `HELLO: /hello.min.a2d1cb24f24b322a7dad520414c523e9.html`)
|
||||
b.AssertFileContent("public/page2/index.html", `HELLO: /hello.min.a2d1cb24f24b322a7dad520414c523e9.html`)
|
||||
b.AssertFileContent("public/hello.json", `
|
||||
integrity": "md5-otHLJPJLMip9rVIEFMUj6Q==
|
||||
mediaType": "text/html
|
||||
relPermalink": "/hello.min.a2d1cb24f24b322a7dad520414c523e9.html"
|
||||
`)
|
||||
}
|
||||
|
||||
func BenchmarkResourceChainPostProcess(b *testing.B) {
|
||||
|
||||
@@ -497,8 +497,6 @@ func (s *shortcodeHandler) renderShortcodesForPage(p *pageState, f output.Format
|
||||
return rendered, hasVariants, nil
|
||||
}
|
||||
|
||||
var errShortCodeIllegalState = errors.New("Illegal shortcode state")
|
||||
|
||||
func (s *shortcodeHandler) parseError(err error, input []byte, pos int) error {
|
||||
if s.p != nil {
|
||||
return s.p.parseError(err, input, pos)
|
||||
@@ -640,7 +638,7 @@ Loop:
|
||||
if params, ok := sc.params.(map[string]any); ok {
|
||||
params[currItem.ValStr(source)] = pt.Next().ValTyped(source)
|
||||
} else {
|
||||
return sc, errShortCodeIllegalState
|
||||
return sc, fmt.Errorf("%s: invalid state: invalid param type %T for shortcode %q, expected a map", errorPrefix, params, sc.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -654,7 +652,7 @@ Loop:
|
||||
params = append(params, currItem.ValTyped(source))
|
||||
sc.params = params
|
||||
} else {
|
||||
return sc, errShortCodeIllegalState
|
||||
return sc, fmt.Errorf("%s: invalid state: invalid param type %T for shortcode %q, expected a slice", errorPrefix, params, sc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1055,3 +1055,35 @@ title: "p1"
|
||||
`)
|
||||
|
||||
}
|
||||
|
||||
// Issue 10236.
|
||||
func TestShortcodeParamEscapedQuote(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
|
||||
{{< figure src="/media/spf13.jpg" title="Steve \"Francia\"." >}}
|
||||
|
||||
-- layouts/shortcodes/figure.html --
|
||||
Title: {{ .Get "title" | safeHTML }}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
Running: true,
|
||||
Verbose: true,
|
||||
},
|
||||
).Build()
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", `Title: Steve "Francia".`)
|
||||
|
||||
}
|
||||
|
||||
@@ -764,8 +764,12 @@ func (s *sitesBuilder) AssertImage(width, height int, filename string) {
|
||||
|
||||
func (s *sitesBuilder) AssertNoDuplicateWrites() {
|
||||
s.Helper()
|
||||
d := s.Fs.PublishDir.(hugofs.DuplicatesReporter)
|
||||
s.Assert(d.ReportDuplicates(), qt.Equals, "")
|
||||
hugofs.WalkFilesystems(s.Fs.PublishDir, func(fs afero.Fs) bool {
|
||||
if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
|
||||
s.Assert(dfs.ReportDuplicates(), qt.Equals, "")
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (s *sitesBuilder) FileContent(filename string) string {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.103.0
|
||||
HUGORELEASER_COMMITISH=beebf2afb09a7be36cf12bdec8a99ae9286504e2
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
project = "hugo"
|
||||
|
||||
# In Hugo v0.103.0 we removed the archive name replacements (e.g. amd64 => 64bit).
|
||||
# Using standard GOOS/GOARCH values makes it easier for scripts out there,
|
||||
# but to prevent breakage in Netlify etc. that has adopted to the old names,
|
||||
# we create aliases for the most common variants.
|
||||
# According to download numbers from v0.101.0, these are by a good margin the two most popular:
|
||||
# hugo_extended_0.101.0_Linux-64bit.tar.gz Downloaded 129,016 times
|
||||
# hugo_0.101.0_Linux-64bit.tar.gz Downloaded 87,846 times
|
||||
# This replacement will create 2 extra alias archives.
|
||||
archive_alias_replacements = { "linux-amd64.tar.gz" = "Linux-64bit.tar.gz" }
|
||||
|
||||
[go_settings]
|
||||
go_proxy = "https://proxy.golang.org"
|
||||
go_exe = "go"
|
||||
|
||||
[build_settings]
|
||||
binary = "hugo"
|
||||
flags = ["-buildmode", "exe"]
|
||||
env = ["CGO_ENABLED=0"]
|
||||
ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio"
|
||||
|
||||
[archive_settings]
|
||||
name_template = "{{ .Project }}_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
extra_files = [
|
||||
{ source_path = "README.md", target_path = "README.md" },
|
||||
{ source_path = "LICENSE", target_path = "LICENSE" },
|
||||
]
|
||||
[archive_settings.type]
|
||||
format = "tar.gz"
|
||||
extension = ".tar.gz"
|
||||
|
||||
[release_settings]
|
||||
name = "${HUGORELEASER_TAG}"
|
||||
type = "github"
|
||||
repository = "hugo"
|
||||
repository_owner = "gohugoio"
|
||||
draft = true
|
||||
prerelease = false
|
||||
|
||||
[release_settings.release_notes_settings]
|
||||
# Use Hugoreleaser's autogenerated release notes.
|
||||
generate = true
|
||||
|
||||
# Collapse relases with < 10 changes below one title.
|
||||
short_threshold = 10
|
||||
short_title = "What's Changed"
|
||||
|
||||
groups = [
|
||||
# Group the changes in the release notes by title.
|
||||
# You need at least one.
|
||||
# The groups will be tested in order until a match is found.
|
||||
# The titles will so be listed in the given order in the release note.
|
||||
# Any match with ignore=true title will be dropped.
|
||||
{ regexp = "Merge commit|Squashed|releaser:", ignore = true },
|
||||
{ title = "Bug fixes", regexp = "fix", ordinal = 10 },
|
||||
{ title = "Dependency Updates", regexp = "deps", ordinal = 30 },
|
||||
{ title = "Build Setup", regexp = "(snap|release|update to)", ordinal = 40 },
|
||||
{ title = "Documentation", regexp = "(doc|readme)", ordinal = 40 },
|
||||
{ title = "Improvements", regexp = ".*", ordinal = 20 },
|
||||
]
|
||||
|
||||
[[builds]]
|
||||
path = "container1/unix/regular"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "darwin"
|
||||
[[builds.os.archs]]
|
||||
goarch = "universal"
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm64"
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm"
|
||||
[builds.os.archs.build_settings]
|
||||
env = ["CGO_ENABLED=0", "GOARM=7"]
|
||||
|
||||
# Unix BSD variants
|
||||
[[builds.os]]
|
||||
goos = "dragonfly"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os]]
|
||||
goos = "freebsd"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os]]
|
||||
goos = "netbsd"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os]]
|
||||
goos = "openbsd"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/unix/extended"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended"]
|
||||
env = ["CGO_ENABLED=1"]
|
||||
|
||||
[[builds.os]]
|
||||
goos = "darwin"
|
||||
[builds.os.build_settings]
|
||||
env = ["CGO_ENABLED=1", "CC=o64-clang", "CXX=o64-clang++"]
|
||||
[[builds.os.archs]]
|
||||
goarch = "universal"
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[builds]]
|
||||
path = "container2/linux/extended"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended"]
|
||||
|
||||
[[builds.os]]
|
||||
goos = "linux"
|
||||
[builds.os.build_settings]
|
||||
env = [
|
||||
"CGO_ENABLED=1",
|
||||
"CC=aarch64-linux-gnu-gcc",
|
||||
"CXX=aarch64-linux-gnu-g++",
|
||||
]
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/windows/regular"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "windows"
|
||||
[builds.os.build_settings]
|
||||
binary = "hugo.exe"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
[[builds.os.archs]]
|
||||
goarch = "arm64"
|
||||
|
||||
[[builds]]
|
||||
path = "container1/windows/extended"
|
||||
|
||||
[builds.build_settings]
|
||||
flags = ["-buildmode", "exe", "-tags", "extended"]
|
||||
env = [
|
||||
"CGO_ENABLED=1",
|
||||
"CC=x86_64-w64-mingw32-gcc",
|
||||
"CXX=x86_64-w64-mingw32-g++",
|
||||
]
|
||||
ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static'"
|
||||
|
||||
[[builds.os]]
|
||||
goos = "windows"
|
||||
[builds.os.build_settings]
|
||||
binary = "hugo.exe"
|
||||
[[builds.os.archs]]
|
||||
goarch = "amd64"
|
||||
|
||||
[[archives]]
|
||||
paths = ["builds/container1/unix/regular/**"]
|
||||
[[archives]]
|
||||
paths = ["builds/container1/unix/extended/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[[archives]]
|
||||
# Only extended builds in container2.
|
||||
paths = ["builds/container2/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[[archives]]
|
||||
paths = ["builds/**/windows/regular/**"]
|
||||
[archives.archive_settings.type]
|
||||
format = "zip"
|
||||
extension = ".zip"
|
||||
[[archives]]
|
||||
paths = ["builds/**/windows/extended/**"]
|
||||
[archives.archive_settings]
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[archives.archive_settings.type]
|
||||
format = "zip"
|
||||
extension = ".zip"
|
||||
[[archives]]
|
||||
paths = ["builds/**/regular/linux/{arm64,amd64}"]
|
||||
[archives.archive_settings]
|
||||
binary_dir = "/usr/local/bin"
|
||||
extra_files = []
|
||||
[archives.archive_settings.type]
|
||||
format = "_plugin"
|
||||
extension = ".deb"
|
||||
[archives.archive_settings.plugin]
|
||||
id = "deb"
|
||||
type = "gorun"
|
||||
command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@v0.6.1"
|
||||
[archives.archive_settings.custom_settings]
|
||||
vendor = "gohugo.io"
|
||||
homepage = "https://github.com/gohugoio/hugoreleaser"
|
||||
maintainer = "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description = "Build, archive and release Go programs."
|
||||
license = "Apache-2.0"
|
||||
[[archives]]
|
||||
paths = ["builds/**/extended/linux/{arm64,amd64}"]
|
||||
[archives.archive_settings]
|
||||
binary_dir = "/usr/local/bin"
|
||||
extra_files = []
|
||||
name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}"
|
||||
[archives.archive_settings.type]
|
||||
format = "_plugin"
|
||||
extension = ".deb"
|
||||
[archives.archive_settings.plugin]
|
||||
id = "deb"
|
||||
type = "gorun"
|
||||
command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@latest"
|
||||
[archives.archive_settings.custom_settings]
|
||||
vendor = "gohugo.io"
|
||||
homepage = "https://github.com/gohugoio/hugoreleaser"
|
||||
maintainer = "Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>"
|
||||
description = "Build, archive and release Go programs."
|
||||
license = "Apache-2.0"
|
||||
|
||||
[[releases]]
|
||||
paths = ["archives/**"]
|
||||
path = "r1"
|
||||
|
||||
# The above should allow the following build commands:
|
||||
# hugoreleaser build -paths "builds/container1/**"
|
||||
# hugoreleaser build -paths "builds/container2/**"
|
||||
# hugoreleaser archive
|
||||
# hugoreleaser release
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -265,6 +265,39 @@ Position: {{ .Position | safeHTML }}
|
||||
b.AssertFileContent("public/p1/index.html", filepath.FromSlash("Position: \"/content/p1.md:7:1\""))
|
||||
}
|
||||
|
||||
// Issue 10118
|
||||
func TestAttributes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
|
||||
## Issue 10118
|
||||
|
||||
§§§ {foo="bar"}
|
||||
Hello, World!
|
||||
§§§
|
||||
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}
|
||||
-- layouts/_default/_markup/render-codeblock.html --
|
||||
Attributes: {{ .Attributes }}|Type: {{ .Type }}|
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "<h2 id=\"issue-10118\">Issue 10118</h2>\nAttributes: map[foo:bar]|Type: |")
|
||||
}
|
||||
|
||||
// Issue 9571
|
||||
func TestAttributesChroma(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -16,6 +16,7 @@ package codeblocks
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
@@ -69,7 +70,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
|
||||
}
|
||||
|
||||
n := node.(*codeBlock)
|
||||
lang := string(n.b.Language(src))
|
||||
lang := getLang(n.b, src)
|
||||
renderer := ctx.RenderContext().GetRenderer(hooks.CodeBlockRendererType, lang)
|
||||
if renderer == nil {
|
||||
return ast.WalkStop, fmt.Errorf("no code renderer found for %q", lang)
|
||||
@@ -174,6 +175,12 @@ func (c *codeBlockContext) Position() htext.Position {
|
||||
return c.pos
|
||||
}
|
||||
|
||||
func getLang(node *ast.FencedCodeBlock, src []byte) string {
|
||||
langWithAttributes := string(node.Language(src))
|
||||
lang, _, _ := strings.Cut(langWithAttributes, "{")
|
||||
return lang
|
||||
}
|
||||
|
||||
func getAttributes(node *ast.FencedCodeBlock, infostr []byte) []ast.Attribute {
|
||||
if node.Attributes() != nil {
|
||||
return node.Attributes()
|
||||
@@ -188,7 +195,7 @@ func getAttributes(node *ast.FencedCodeBlock, infostr []byte) []ast.Attribute {
|
||||
}
|
||||
}
|
||||
|
||||
if attrStartIdx > 0 {
|
||||
if attrStartIdx != -1 {
|
||||
n := ast.NewTextBlock() // dummy node for storing attributes
|
||||
attrStr := infostr[attrStartIdx:]
|
||||
if attrs, hasAttr := parser.ParseAttributes(text.NewReader(attrStr)); hasAttr {
|
||||
|
||||
@@ -194,7 +194,12 @@ func (l *pageLexer) ignoreEscapesAndEmit(t ItemType, isString bool) {
|
||||
if i > k {
|
||||
segments = append(segments, lowHigh{k, i})
|
||||
}
|
||||
l.append(Item{Type: TypeIgnore, low: i, high: i + w})
|
||||
// See issue #10236.
|
||||
// We don't send the backslash back to the client,
|
||||
// which makes the end parsing simpler.
|
||||
// This means that we cannot render the AST back to be
|
||||
// exactly the same as the input,
|
||||
// but that was also the situation before we introduced the issue in #10236.
|
||||
k = i + w
|
||||
}
|
||||
i += w
|
||||
|
||||
@@ -40,7 +40,6 @@ var (
|
||||
tstParamFloat = nti(tScParam, "3.14")
|
||||
tstVal = nti(tScParamVal, "Hello World")
|
||||
tstText = nti(tText, "Hello World")
|
||||
tstIgnoreEscape = nti(TypeIgnore, "\\")
|
||||
)
|
||||
|
||||
var shortCodeLexerTests = []lexerTest{
|
||||
@@ -179,14 +178,14 @@ var shortCodeLexerTests = []lexerTest{
|
||||
"escaped quotes inside nonescaped quotes",
|
||||
`{{< sc1 param1="Hello \"escaped\" World" >}}`,
|
||||
[]typeText{
|
||||
tstLeftNoMD, tstSC1, tstParam1, tstIgnoreEscape, tstIgnoreEscape, nti(tScParamVal, `Hello "escaped" World`), tstRightNoMD, tstEOF,
|
||||
tstLeftNoMD, tstSC1, tstParam1, nti(tScParamVal, `Hello "escaped" World`), tstRightNoMD, tstEOF,
|
||||
},
|
||||
},
|
||||
{
|
||||
"escaped quotes inside nonescaped quotes in positional param",
|
||||
`{{< sc1 "Hello \"escaped\" World" >}}`,
|
||||
[]typeText{
|
||||
tstLeftNoMD, tstSC1, tstIgnoreEscape, tstIgnoreEscape, nti(tScParam, `Hello "escaped" World`), tstRightNoMD, tstEOF,
|
||||
tstLeftNoMD, tstSC1, nti(tScParam, `Hello "escaped" World`), tstRightNoMD, tstEOF,
|
||||
},
|
||||
},
|
||||
{"escaped raw string, named param", `{{< sc1 param1=` + `\` + "`" + "Hello World" + `\` + "`" + ` >}}`, []typeText{
|
||||
|
||||
-253
@@ -1,253 +0,0 @@
|
||||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
)
|
||||
|
||||
var issueRe = regexp.MustCompile(`(?i)(?:Updates?|Closes?|Fix.*|See) #(\d+)`)
|
||||
|
||||
type changeLog struct {
|
||||
Version string
|
||||
Notes gitInfos
|
||||
All gitInfos
|
||||
Docs gitInfos
|
||||
|
||||
// Overall stats
|
||||
Repo *gitHubRepo
|
||||
ContributorCount int
|
||||
ThemeCount int
|
||||
}
|
||||
|
||||
func newChangeLog(infos, docInfos gitInfos) *changeLog {
|
||||
log := &changeLog{
|
||||
Docs: docInfos,
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
// TODO(bep) improve
|
||||
if regexp.MustCompile("(?i)deprecate|note").MatchString(info.Subject) {
|
||||
log.Notes = append(log.Notes, info)
|
||||
}
|
||||
|
||||
log.All = append(log.All, info)
|
||||
info.Subject = strings.TrimSpace(info.Subject)
|
||||
|
||||
}
|
||||
|
||||
return log
|
||||
}
|
||||
|
||||
type gitInfo struct {
|
||||
Hash string
|
||||
Author string
|
||||
Subject string
|
||||
Body string
|
||||
|
||||
GitHubCommit *gitHubCommit
|
||||
}
|
||||
|
||||
func (g gitInfo) Issues() []int {
|
||||
return extractIssues(g.Body)
|
||||
}
|
||||
|
||||
func (g gitInfo) AuthorID() string {
|
||||
if g.GitHubCommit != nil {
|
||||
return g.GitHubCommit.Author.Login
|
||||
}
|
||||
return g.Author
|
||||
}
|
||||
|
||||
func extractIssues(body string) []int {
|
||||
var i []int
|
||||
m := issueRe.FindAllStringSubmatch(body, -1)
|
||||
for _, mm := range m {
|
||||
issueID, err := strconv.Atoi(mm[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
i = append(i, issueID)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
type gitInfos []gitInfo
|
||||
|
||||
func git(args ...string) (string, error) {
|
||||
cmd, _ := hexec.SafeCommand("git", args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args)
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func getGitInfos(tag, repo, repoPath string, remote bool) (gitInfos, error) {
|
||||
return getGitInfosBefore("HEAD", tag, repo, repoPath, remote)
|
||||
}
|
||||
|
||||
type countribCount struct {
|
||||
Author string
|
||||
GitHubAuthor gitHubAuthor
|
||||
Count int
|
||||
}
|
||||
|
||||
func (c countribCount) AuthorLink() string {
|
||||
if c.GitHubAuthor.HTMLURL != "" {
|
||||
return fmt.Sprintf("[@%s](%s)", c.GitHubAuthor.Login, c.GitHubAuthor.HTMLURL)
|
||||
}
|
||||
|
||||
if !strings.Contains(c.Author, "@") {
|
||||
return c.Author
|
||||
}
|
||||
|
||||
return c.Author[:strings.Index(c.Author, "@")]
|
||||
}
|
||||
|
||||
type contribCounts []countribCount
|
||||
|
||||
func (c contribCounts) Less(i, j int) bool { return c[i].Count > c[j].Count }
|
||||
func (c contribCounts) Len() int { return len(c) }
|
||||
func (c contribCounts) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
|
||||
func (g gitInfos) ContribCountPerAuthor() contribCounts {
|
||||
var c contribCounts
|
||||
|
||||
counters := make(map[string]countribCount)
|
||||
|
||||
for _, gi := range g {
|
||||
authorID := gi.AuthorID()
|
||||
if count, ok := counters[authorID]; ok {
|
||||
count.Count = count.Count + 1
|
||||
counters[authorID] = count
|
||||
} else {
|
||||
var ghA gitHubAuthor
|
||||
if gi.GitHubCommit != nil {
|
||||
ghA = gi.GitHubCommit.Author
|
||||
}
|
||||
authorCount := countribCount{Count: 1, Author: gi.Author, GitHubAuthor: ghA}
|
||||
counters[authorID] = authorCount
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range counters {
|
||||
c = append(c, v)
|
||||
}
|
||||
|
||||
sort.Sort(c)
|
||||
return c
|
||||
}
|
||||
|
||||
func getGitInfosBefore(ref, tag, repo, repoPath string, remote bool) (gitInfos, error) {
|
||||
client := newGitHubAPI(repo)
|
||||
var g gitInfos
|
||||
|
||||
log, err := gitLogBefore(ref, tag, repoPath)
|
||||
if err != nil {
|
||||
return g, err
|
||||
}
|
||||
|
||||
log = strings.Trim(log, "\n\x1e'")
|
||||
entries := strings.Split(log, "\x1e")
|
||||
|
||||
for _, entry := range entries {
|
||||
items := strings.Split(entry, "\x1f")
|
||||
gi := gitInfo{}
|
||||
|
||||
if len(items) > 0 {
|
||||
gi.Hash = items[0]
|
||||
}
|
||||
if len(items) > 1 {
|
||||
gi.Author = items[1]
|
||||
}
|
||||
if len(items) > 2 {
|
||||
gi.Subject = items[2]
|
||||
}
|
||||
if len(items) > 3 {
|
||||
gi.Body = items[3]
|
||||
}
|
||||
|
||||
if remote && gi.Hash != "" {
|
||||
gc, err := client.fetchCommit(gi.Hash)
|
||||
if err == nil {
|
||||
gi.GitHubCommit = &gc
|
||||
}
|
||||
}
|
||||
g = append(g, gi)
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// Ignore autogenerated commits etc. in change log. This is a regexp.
|
||||
const ignoredCommits = "snapcraft:|Merge commit|Squashed"
|
||||
|
||||
func gitLogBefore(ref, tag, repoPath string) (string, error) {
|
||||
var prevTag string
|
||||
var err error
|
||||
if tag != "" {
|
||||
prevTag = tag
|
||||
} else {
|
||||
prevTag, err = gitVersionTagBefore(ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
defaultArgs := []string{"log", "-E", fmt.Sprintf("--grep=%s", ignoredCommits), "--invert-grep", "--pretty=format:%x1e%h%x1f%aE%x1f%s%x1f%b", "--abbrev-commit", prevTag + ".." + ref}
|
||||
|
||||
var args []string
|
||||
|
||||
if repoPath != "" {
|
||||
args = append([]string{"-C", repoPath}, defaultArgs...)
|
||||
} else {
|
||||
args = defaultArgs
|
||||
}
|
||||
|
||||
log, err := git(args...)
|
||||
if err != nil {
|
||||
return ",", err
|
||||
}
|
||||
|
||||
return log, err
|
||||
}
|
||||
|
||||
func gitVersionTagBefore(ref string) (string, error) {
|
||||
return gitShort("describe", "--tags", "--abbrev=0", "--always", "--match", "v[0-9]*", ref+"^")
|
||||
}
|
||||
|
||||
func gitShort(args ...string) (output string, err error) {
|
||||
output, err = git(args...)
|
||||
return strings.Replace(strings.Split(output, "\n")[0], "'", "", -1), err
|
||||
}
|
||||
|
||||
func tagExists(tag string) (bool, error) {
|
||||
out, err := git("tag", "-l", tag)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if strings.Contains(out, tag) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestGitInfos(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
skipIfCI(t)
|
||||
infos, err := getGitInfos("v0.20", "hugo", "", false)
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(len(infos) > 0, qt.Equals, true)
|
||||
}
|
||||
|
||||
func TestIssuesRe(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
body := `
|
||||
This is a commit message.
|
||||
|
||||
Updates #123
|
||||
Fix #345
|
||||
closes #543
|
||||
See #456
|
||||
`
|
||||
|
||||
issues := extractIssues(body)
|
||||
|
||||
c.Assert(len(issues), qt.Equals, 4)
|
||||
c.Assert(issues[0], qt.Equals, 123)
|
||||
c.Assert(issues[2], qt.Equals, 543)
|
||||
|
||||
bodyNoIssues := `
|
||||
This is a commit message without issue refs.
|
||||
|
||||
But it has e #10 to make old regexp confused.
|
||||
Streets #20.
|
||||
`
|
||||
|
||||
emptyIssuesList := extractIssues(bodyNoIssues)
|
||||
c.Assert(len(emptyIssuesList), qt.Equals, 0)
|
||||
}
|
||||
|
||||
func TestGitVersionTagBefore(t *testing.T) {
|
||||
skipIfCI(t)
|
||||
c := qt.New(t)
|
||||
v1, err := gitVersionTagBefore("v0.18")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(v1, qt.Equals, "v0.17")
|
||||
}
|
||||
|
||||
func TestTagExists(t *testing.T) {
|
||||
skipIfCI(t)
|
||||
c := qt.New(t)
|
||||
b1, err := tagExists("v0.18")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(b1, qt.Equals, true)
|
||||
|
||||
b2, err := tagExists("adfagdsfg")
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(b2, qt.Equals, false)
|
||||
}
|
||||
|
||||
func skipIfCI(t *testing.T) {
|
||||
if isCI() {
|
||||
// Travis has an ancient git with no --invert-grep: https://github.com/travis-ci/travis-ci/issues/6328
|
||||
// Also Travis clones very shallowly, making some of the tests above shaky.
|
||||
t.Skip("Skip git test on Linux to make Travis happy.")
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
gitHubCommitsAPI = "https://api.github.com/repos/gohugoio/REPO/commits/%s"
|
||||
gitHubRepoAPI = "https://api.github.com/repos/gohugoio/REPO"
|
||||
gitHubContributorsAPI = "https://api.github.com/repos/gohugoio/REPO/contributors"
|
||||
)
|
||||
|
||||
type gitHubAPI struct {
|
||||
commitsAPITemplate string
|
||||
repoAPI string
|
||||
contributorsAPITemplate string
|
||||
}
|
||||
|
||||
func newGitHubAPI(repo string) *gitHubAPI {
|
||||
return &gitHubAPI{
|
||||
commitsAPITemplate: strings.Replace(gitHubCommitsAPI, "REPO", repo, -1),
|
||||
repoAPI: strings.Replace(gitHubRepoAPI, "REPO", repo, -1),
|
||||
contributorsAPITemplate: strings.Replace(gitHubContributorsAPI, "REPO", repo, -1),
|
||||
}
|
||||
}
|
||||
|
||||
type gitHubCommit struct {
|
||||
Author gitHubAuthor `json:"author"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
}
|
||||
|
||||
type gitHubAuthor struct {
|
||||
ID int `json:"id"`
|
||||
Login string `json:"login"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
}
|
||||
|
||||
type gitHubRepo struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Stars int `json:"stargazers_count"`
|
||||
Contributors []gitHubContributor
|
||||
}
|
||||
|
||||
type gitHubContributor struct {
|
||||
ID int `json:"id"`
|
||||
Login string `json:"login"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Contributions int `json:"contributions"`
|
||||
}
|
||||
|
||||
func (g *gitHubAPI) fetchCommit(ref string) (gitHubCommit, error) {
|
||||
var commit gitHubCommit
|
||||
|
||||
u := fmt.Sprintf(g.commitsAPITemplate, ref)
|
||||
|
||||
req, err := http.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return commit, err
|
||||
}
|
||||
|
||||
err = doGitHubRequest(req, &commit)
|
||||
|
||||
return commit, err
|
||||
}
|
||||
|
||||
func (g *gitHubAPI) fetchRepo() (gitHubRepo, error) {
|
||||
var repo gitHubRepo
|
||||
|
||||
req, err := http.NewRequest("GET", g.repoAPI, nil)
|
||||
if err != nil {
|
||||
return repo, err
|
||||
}
|
||||
|
||||
err = doGitHubRequest(req, &repo)
|
||||
if err != nil {
|
||||
return repo, err
|
||||
}
|
||||
|
||||
var contributors []gitHubContributor
|
||||
page := 0
|
||||
for {
|
||||
page++
|
||||
var currPage []gitHubContributor
|
||||
url := fmt.Sprintf(g.contributorsAPITemplate+"?page=%d", page)
|
||||
|
||||
req, err = http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return repo, err
|
||||
}
|
||||
|
||||
err = doGitHubRequest(req, &currPage)
|
||||
if err != nil {
|
||||
return repo, err
|
||||
}
|
||||
if len(currPage) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
contributors = append(contributors, currPage...)
|
||||
|
||||
}
|
||||
|
||||
repo.Contributors = contributors
|
||||
|
||||
return repo, err
|
||||
}
|
||||
|
||||
func doGitHubRequest(req *http.Request, v any) error {
|
||||
addGitHubToken(req)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if isError(resp) {
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
return fmt.Errorf("GitHub lookup failed: %s", string(b))
|
||||
}
|
||||
|
||||
return json.NewDecoder(resp.Body).Decode(v)
|
||||
}
|
||||
|
||||
func isError(resp *http.Response) bool {
|
||||
return resp.StatusCode < 200 || resp.StatusCode > 299
|
||||
}
|
||||
|
||||
func addGitHubToken(req *http.Request) {
|
||||
gitHubToken := os.Getenv("GITHUB_TOKEN")
|
||||
if gitHubToken != "" {
|
||||
req.Header.Add("Authorization", "token "+gitHubToken)
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestGitHubLookupCommit(t *testing.T) {
|
||||
skipIfNoToken(t)
|
||||
c := qt.New(t)
|
||||
client := newGitHubAPI("hugo")
|
||||
commit, err := client.fetchCommit("793554108763c0984f1a1b1a6ee5744b560d78d0")
|
||||
c.Assert(err, qt.IsNil)
|
||||
fmt.Println(commit)
|
||||
}
|
||||
|
||||
func TestFetchRepo(t *testing.T) {
|
||||
skipIfNoToken(t)
|
||||
c := qt.New(t)
|
||||
client := newGitHubAPI("hugo")
|
||||
repo, err := client.fetchRepo()
|
||||
c.Assert(err, qt.IsNil)
|
||||
fmt.Println(">>", len(repo.Contributors))
|
||||
}
|
||||
|
||||
func skipIfNoToken(t *testing.T) {
|
||||
if os.Getenv("GITHUB_TOKEN") == "" {
|
||||
t.Skip("Skip test against GitHub as no GITHUB_TOKEN set.")
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package releaser implements a set of utilities and a wrapper around Goreleaser
|
||||
// to help automate the Hugo release process.
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
issueLinkTemplate = "#%d"
|
||||
linkTemplate = "[%s](%s)"
|
||||
releaseNotesMarkdownTemplatePatchRelease = `
|
||||
{{ if eq (len .All) 1 }}
|
||||
This is a bug-fix release with one important fix.
|
||||
{{ else }}
|
||||
This is a bug-fix release with a couple of important fixes.
|
||||
{{ end }}
|
||||
{{ range .All }}
|
||||
{{- if .GitHubCommit -}}
|
||||
* {{ .Subject }} {{ .Hash }} {{ . | author }} {{ range .Issues }}{{ . | issue }} {{ end }}
|
||||
{{ else -}}
|
||||
* {{ .Subject }} {{ range .Issues }}{{ . | issue }} {{ end }}
|
||||
{{ end -}}
|
||||
{{- end }}
|
||||
|
||||
|
||||
`
|
||||
releaseNotesMarkdownTemplate = `
|
||||
{{- $contribsPerAuthor := .All.ContribCountPerAuthor -}}
|
||||
{{- $docsContribsPerAuthor := .Docs.ContribCountPerAuthor -}}
|
||||
|
||||
This release represents **{{ len .All }} contributions by {{ len $contribsPerAuthor }} contributors** to the main Hugo code base.
|
||||
|
||||
{{- if gt (len $contribsPerAuthor) 3 -}}
|
||||
{{- $u1 := index $contribsPerAuthor 0 -}}
|
||||
{{- $u2 := index $contribsPerAuthor 1 -}}
|
||||
{{- $u3 := index $contribsPerAuthor 2 -}}
|
||||
{{- $u4 := index $contribsPerAuthor 3 -}}
|
||||
{{- $u1.AuthorLink }} leads the Hugo development with a significant amount of contributions, but also a big shoutout to {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their ongoing contributions.
|
||||
And thanks to [@digitalcraftsman](https://github.com/digitalcraftsman) for his ongoing work on keeping the themes site in pristine condition.
|
||||
{{ end }}
|
||||
Many have also been busy writing and fixing the documentation in [hugoDocs](https://github.com/gohugoio/hugoDocs),
|
||||
which has received **{{ len .Docs }} contributions by {{ len $docsContribsPerAuthor }} contributors**.
|
||||
{{- if gt (len $docsContribsPerAuthor) 3 -}}
|
||||
{{- $u1 := index $docsContribsPerAuthor 0 -}}
|
||||
{{- $u2 := index $docsContribsPerAuthor 1 -}}
|
||||
{{- $u3 := index $docsContribsPerAuthor 2 -}}
|
||||
{{- $u4 := index $docsContribsPerAuthor 3 }} A special thanks to {{ $u1.AuthorLink }}, {{ $u2.AuthorLink }}, {{ $u3.AuthorLink }}, and {{ $u4.AuthorLink }} for their work on the documentation site.
|
||||
{{ end }}
|
||||
|
||||
Hugo now has:
|
||||
|
||||
{{ with .Repo -}}
|
||||
* {{ .Stars }}+ [stars](https://github.com/gohugoio/hugo/stargazers)
|
||||
* {{ len .Contributors }}+ [contributors](https://github.com/gohugoio/hugo/graphs/contributors)
|
||||
{{- end -}}
|
||||
{{ with .ThemeCount }}
|
||||
* {{ . }}+ [themes](http://themes.gohugo.io/)
|
||||
{{ end }}
|
||||
{{ with .Notes }}
|
||||
## Notes
|
||||
{{ template "change-section" . }}
|
||||
{{- end -}}
|
||||
{{ with .All }}
|
||||
## Changes
|
||||
{{ template "change-section" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "change-section" }}
|
||||
{{ range . }}
|
||||
{{- if .GitHubCommit -}}
|
||||
* {{ .Subject }} {{ .Hash }} {{ . | author }} {{ range .Issues }}{{ . | issue }} {{ end }}
|
||||
{{ else -}}
|
||||
* {{ .Subject }} {{ range .Issues }}{{ . | issue }} {{ end }}
|
||||
{{ end -}}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
`
|
||||
)
|
||||
|
||||
var templateFuncs = template.FuncMap{
|
||||
"isPatch": func(c changeLog) bool {
|
||||
return !strings.HasSuffix(c.Version, "0")
|
||||
},
|
||||
"issue": func(id int) string {
|
||||
return fmt.Sprintf(issueLinkTemplate, id)
|
||||
},
|
||||
"commitURL": func(info gitInfo) string {
|
||||
if info.GitHubCommit.HTMLURL == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(linkTemplate, info.Hash, info.GitHubCommit.HTMLURL)
|
||||
},
|
||||
"author": func(info gitInfo) string {
|
||||
return "@" + info.GitHubCommit.Author.Login
|
||||
},
|
||||
}
|
||||
|
||||
func writeReleaseNotes(version string, infosMain, infosDocs gitInfos, to io.Writer) error {
|
||||
client := newGitHubAPI("hugo")
|
||||
changes := newChangeLog(infosMain, infosDocs)
|
||||
changes.Version = version
|
||||
repo, err := client.fetchRepo()
|
||||
if err == nil {
|
||||
changes.Repo = &repo
|
||||
}
|
||||
themeCount, err := fetchThemeCount()
|
||||
if err == nil {
|
||||
changes.ThemeCount = themeCount
|
||||
}
|
||||
|
||||
mtempl := releaseNotesMarkdownTemplate
|
||||
|
||||
if !strings.HasSuffix(version, "0") {
|
||||
mtempl = releaseNotesMarkdownTemplatePatchRelease
|
||||
}
|
||||
|
||||
tmpl, err := template.New("").Funcs(templateFuncs).Parse(mtempl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tmpl.Execute(to, changes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchThemeCount() (int, error) {
|
||||
resp, err := http.Get("https://raw.githubusercontent.com/gohugoio/hugoThemesSiteBuilder/main/themes.txt")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
return bytes.Count(b, []byte("\n")) - bytes.Count(b, []byte("#")), nil
|
||||
}
|
||||
|
||||
func getReleaseNotesFilename(version string) string {
|
||||
return filepath.FromSlash(fmt.Sprintf("temp/%s-relnotes-ready.md", version))
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) writeReleaseNotesToTemp(version string, isPatch bool, infosMain, infosDocs gitInfos) (string, error) {
|
||||
filename := getReleaseNotesFilename(version)
|
||||
|
||||
var w io.WriteCloser
|
||||
|
||||
if !r.try {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
w = f
|
||||
|
||||
} else {
|
||||
w = os.Stdout
|
||||
}
|
||||
|
||||
if err := writeReleaseNotes(version, infosMain, infosDocs, w); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright 2017-present The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package commands defines and implements command-line commands and flags
|
||||
// used by Hugo. Commands and flags are implemented using Cobra.
|
||||
|
||||
package releaser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func _TestReleaseNotesWriter(t *testing.T) {
|
||||
skipIfNoToken(t)
|
||||
if os.Getenv("CI") != "" {
|
||||
// Travis has an ancient git with no --invert-grep: https://github.com/travis-ci/travis-ci/issues/6328
|
||||
t.Skip("Skip git test on CI to make Travis happy..")
|
||||
}
|
||||
|
||||
c := qt.New(t)
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
// TODO(bep) consider to query GitHub directly for the gitlog with author info, probably faster.
|
||||
infos, err := getGitInfosBefore("HEAD", "v0.89.0", "hugo", "", false)
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
c.Assert(writeReleaseNotes("0.89.0", infos, infos, &b), qt.IsNil)
|
||||
|
||||
fmt.Println(b.String())
|
||||
}
|
||||
+105
-178
@@ -17,7 +17,6 @@ package releaser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -25,22 +24,59 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
)
|
||||
|
||||
const commitPrefix = "releaser:"
|
||||
|
||||
// New initialises a ReleaseHandler.
|
||||
func New(skipPush, try bool, step int) (*ReleaseHandler, error) {
|
||||
if step < 1 || step > 2 {
|
||||
return nil, fmt.Errorf("step must be 1 or 2")
|
||||
}
|
||||
|
||||
prefix := "release-"
|
||||
branch, err := git("rev-parse", "--abbrev-ref", "HEAD")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
branch = strings.TrimSpace(branch)
|
||||
|
||||
if !strings.HasPrefix(branch, prefix) {
|
||||
return nil, fmt.Errorf("branch %q is not a release branch", branch)
|
||||
}
|
||||
|
||||
version := strings.TrimPrefix(branch, prefix)
|
||||
version = strings.TrimPrefix(version, "v")
|
||||
|
||||
logf("Branch: %s|Version: v%s\n", branch, version)
|
||||
|
||||
rh := &ReleaseHandler{branchVersion: version, skipPush: skipPush, try: try, step: step}
|
||||
|
||||
if try {
|
||||
rh.git = func(args ...string) (string, error) {
|
||||
logln("git", strings.Join(args, " "))
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
rh.git = git
|
||||
}
|
||||
|
||||
return rh, nil
|
||||
}
|
||||
|
||||
// ReleaseHandler provides functionality to release a new version of Hugo.
|
||||
// Test this locally without doing an actual release:
|
||||
// go run -tags release main.go release --skip-publish --try -r 0.90.0
|
||||
// Or a variation of the above -- the skip-publish flag makes sure that any changes are performed to the local Git only.
|
||||
type ReleaseHandler struct {
|
||||
cliVersion string
|
||||
branchVersion string
|
||||
|
||||
skipPublish bool
|
||||
// 1 or 2.
|
||||
step int
|
||||
|
||||
// No remote pushes.
|
||||
skipPush bool
|
||||
|
||||
// Just simulate, no actual changes.
|
||||
try bool
|
||||
@@ -48,144 +84,48 @@ type ReleaseHandler struct {
|
||||
git func(args ...string) (string, error)
|
||||
}
|
||||
|
||||
func (r ReleaseHandler) calculateVersions() (hugo.Version, hugo.Version) {
|
||||
newVersion := hugo.MustParseVersion(r.cliVersion)
|
||||
finalVersion := newVersion.Next()
|
||||
finalVersion.PatchLevel = 0
|
||||
|
||||
if newVersion.Suffix != "-test" {
|
||||
newVersion.Suffix = ""
|
||||
}
|
||||
|
||||
finalVersion.Suffix = "-DEV"
|
||||
|
||||
return newVersion, finalVersion
|
||||
}
|
||||
|
||||
// New initialises a ReleaseHandler.
|
||||
func New(version string, skipPublish, try bool) *ReleaseHandler {
|
||||
// When triggered from CI release branch
|
||||
version = strings.TrimPrefix(version, "release-")
|
||||
version = strings.TrimPrefix(version, "v")
|
||||
rh := &ReleaseHandler{cliVersion: version, skipPublish: skipPublish, try: try}
|
||||
|
||||
if try {
|
||||
rh.git = func(args ...string) (string, error) {
|
||||
fmt.Println("git", strings.Join(args, " "))
|
||||
return "", nil
|
||||
}
|
||||
} else {
|
||||
rh.git = git
|
||||
}
|
||||
|
||||
return rh
|
||||
}
|
||||
|
||||
// Run creates a new release.
|
||||
func (r *ReleaseHandler) Run() error {
|
||||
if os.Getenv("GITHUB_TOKEN") == "" {
|
||||
return errors.New("GITHUB_TOKEN not set, create one here with the repo scope selected: https://github.com/settings/tokens/new")
|
||||
}
|
||||
|
||||
fmt.Printf("Start release from %q\n", wd())
|
||||
|
||||
newVersion, finalVersion := r.calculateVersions()
|
||||
|
||||
version := newVersion.String()
|
||||
tag := "v" + version
|
||||
isPatch := newVersion.PatchLevel > 0
|
||||
mainVersion := newVersion
|
||||
mainVersion.PatchLevel = 0
|
||||
|
||||
// Exit early if tag already exists
|
||||
exists, err := tagExists(tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.gitPush()
|
||||
|
||||
if exists {
|
||||
return fmt.Errorf("tag %q already exists", tag)
|
||||
}
|
||||
|
||||
var changeLogFromTag string
|
||||
|
||||
if newVersion.PatchLevel == 0 {
|
||||
// There may have been patch releases between, so set the tag explicitly.
|
||||
changeLogFromTag = "v" + newVersion.Prev().String()
|
||||
exists, _ := tagExists(changeLogFromTag)
|
||||
if !exists {
|
||||
// fall back to one that exists.
|
||||
changeLogFromTag = ""
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
gitCommits gitInfos
|
||||
gitCommitsDocs gitInfos
|
||||
)
|
||||
|
||||
defer r.gitPush() // TODO(bep)
|
||||
|
||||
gitCommits, err = getGitInfos(changeLogFromTag, "hugo", "", !r.try)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(bep) explicit tag?
|
||||
gitCommitsDocs, err = getGitInfos("", "hugoDocs", "../hugoDocs", !r.try)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
releaseNotesFile, err := r.writeReleaseNotesToTemp(version, isPatch, gitCommits, gitCommitsDocs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := r.git("add", releaseNotesFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commitMsg := fmt.Sprintf("%s Add release notes for %s", commitPrefix, newVersion)
|
||||
commitMsg += "\n[ci skip]"
|
||||
|
||||
if _, err := r.git("commit", "-m", commitMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.bumpVersions(newVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := r.git("commit", "-a", "-m", fmt.Sprintf("%s Bump versions for release of %s\n\n[ci skip]", commitPrefix, newVersion)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := r.git("tag", "-a", tag, "-m", fmt.Sprintf("%s %s\n\n[ci skip]", commitPrefix, newVersion)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.skipPublish {
|
||||
if _, err := r.git("push", "origin", tag); err != nil {
|
||||
if r.step == 1 {
|
||||
if err := r.bumpVersions(newVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.release(releaseNotesFile); err != nil {
|
||||
return err
|
||||
if _, err := r.git("commit", "-a", "-m", fmt.Sprintf("%s Bump versions for release of %s\n\n[ci skip]", commitPrefix, newVersion)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The above commit will be the target for this release, so print it to the console in a env friendly way.
|
||||
sha, err := git("rev-parse", "HEAD")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Hugoreleaser will do the actual release using these values.
|
||||
if err := r.replaceInFile("hugoreleaser.env",
|
||||
`HUGORELEASER_TAG=(\S*)`, "HUGORELEASER_TAG="+tag,
|
||||
`HUGORELEASER_COMMITISH=(\S*)`, "HUGORELEASER_COMMITISH="+sha,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
logf("HUGORELEASER_TAG=%s\n", tag)
|
||||
logf("HUGORELEASER_COMMITISH=%s\n", sha)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := r.bumpVersions(finalVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.try {
|
||||
// No longer needed.
|
||||
if err := os.Remove(releaseNotesFile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := r.git("commit", "-a", "-m", fmt.Sprintf("%s Prepare repository for %s\n\n[ci skip]", commitPrefix, finalVersion)); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -193,36 +133,6 @@ func (r *ReleaseHandler) Run() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) gitPush() {
|
||||
if r.skipPublish {
|
||||
return
|
||||
}
|
||||
if _, err := r.git("push", "origin", "HEAD"); err != nil {
|
||||
log.Fatal("push failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) release(releaseNotesFile string) error {
|
||||
if r.try {
|
||||
fmt.Println("Skip goreleaser...")
|
||||
return nil
|
||||
}
|
||||
|
||||
args := []string{"--parallelism", "2", "--timeout", "120m", "--rm-dist", "--release-notes", releaseNotesFile}
|
||||
if r.skipPublish {
|
||||
args = append(args, "--skip-publish")
|
||||
}
|
||||
|
||||
cmd, _ := hexec.SafeCommand("goreleaser", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("goreleaser failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) bumpVersions(ver hugo.Version) error {
|
||||
toDev := ""
|
||||
|
||||
@@ -237,16 +147,6 @@ func (r *ReleaseHandler) bumpVersions(ver hugo.Version) error {
|
||||
return err
|
||||
}
|
||||
|
||||
snapcraftGrade := "stable"
|
||||
if ver.Suffix != "" {
|
||||
snapcraftGrade = "devel"
|
||||
}
|
||||
if err := r.replaceInFile("snap/snapcraft.yaml",
|
||||
`version: "(.*)"`, fmt.Sprintf(`version: "%s"`, ver),
|
||||
`grade: (.*) #`, fmt.Sprintf(`grade: %s #`, snapcraftGrade)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var minVersion string
|
||||
if ver.Suffix != "" {
|
||||
// People use the DEV version in daily use, and we cannot create new themes
|
||||
@@ -264,6 +164,29 @@ func (r *ReleaseHandler) bumpVersions(ver hugo.Version) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r ReleaseHandler) calculateVersions() (hugo.Version, hugo.Version) {
|
||||
newVersion := hugo.MustParseVersion(r.branchVersion)
|
||||
finalVersion := newVersion.Next()
|
||||
finalVersion.PatchLevel = 0
|
||||
|
||||
if newVersion.Suffix != "-test" {
|
||||
newVersion.Suffix = ""
|
||||
}
|
||||
|
||||
finalVersion.Suffix = "-DEV"
|
||||
|
||||
return newVersion, finalVersion
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) gitPush() {
|
||||
if r.skipPush {
|
||||
return
|
||||
}
|
||||
if _, err := r.git("push", "origin", "HEAD"); err != nil {
|
||||
log.Fatal("push failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error {
|
||||
filename = filepath.FromSlash(filename)
|
||||
fi, err := os.Stat(filename)
|
||||
@@ -272,11 +195,11 @@ func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error
|
||||
}
|
||||
|
||||
if r.try {
|
||||
fmt.Printf("Replace in %q: %q\n", filename, oldNew)
|
||||
logf("Replace in %q: %q\n", filename, oldNew)
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -287,18 +210,22 @@ func (r *ReleaseHandler) replaceInFile(filename string, oldNew ...string) error
|
||||
newContent = re.ReplaceAllString(newContent, oldNew[i+1])
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename, []byte(newContent), fi.Mode())
|
||||
return os.WriteFile(filename, []byte(newContent), fi.Mode())
|
||||
}
|
||||
|
||||
func isCI() bool {
|
||||
return os.Getenv("CI") != ""
|
||||
}
|
||||
|
||||
func wd() string {
|
||||
p, err := os.Getwd()
|
||||
func git(args ...string) (string, error) {
|
||||
cmd, _ := hexec.SafeCommand("git", args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args)
|
||||
}
|
||||
return p
|
||||
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func logf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
}
|
||||
|
||||
func logln(args ...interface{}) {
|
||||
fmt.Fprintln(os.Stderr, args...)
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||
// This is a workaround for what looks like a bug in Libsass. But
|
||||
// getting this resolution correct in tools like Chrome Workspaces
|
||||
// is important enough to go this extra mile.
|
||||
mapContent := strings.Replace(res.SourceMapContent, `stdin",`, fmt.Sprintf("%s\",", sourcePath), 1)
|
||||
mapContent := strings.Replace(res.SourceMapContent, `stdin"`, fmt.Sprintf("%s\"", sourcePath), 1)
|
||||
|
||||
return ctx.PublishSourceMap(mapContent)
|
||||
}
|
||||
|
||||
@@ -71,8 +71,12 @@ func TestTransform(t *testing.T) {
|
||||
// Verify that we publish the same file once only.
|
||||
assertNoDuplicateWrites := func(c *qt.C, spec *Spec) {
|
||||
c.Helper()
|
||||
d := spec.Fs.PublishDir.(hugofs.DuplicatesReporter)
|
||||
c.Assert(d.ReportDuplicates(), qt.Equals, "")
|
||||
hugofs.WalkFilesystems(spec.Fs.PublishDir, func(fs afero.Fs) bool {
|
||||
if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
|
||||
c.Assert(dfs.ReportDuplicates(), qt.Equals, "")
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
assertShouldExist := func(c *qt.C, spec *Spec, filename string, should bool) {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
options:
|
||||
source:
|
||||
required: true
|
||||
source-type:
|
||||
source-tag:
|
||||
source-branch:
|
||||
nodejs-target:
|
||||
required: true
|
||||
@@ -1,332 +0,0 @@
|
||||
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
|
||||
#
|
||||
# Modified by Anthony Fok on 2018-10-01 to add support for ppc64el and s390x
|
||||
#
|
||||
# Copyright (C) 2015-2017 Canonical Ltd
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 3 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""The nodejs plugin is useful for node/npm based parts.
|
||||
|
||||
The plugin uses node to install dependencies from `package.json`. It
|
||||
also sets up binaries defined in `package.json` into the `PATH`.
|
||||
|
||||
This plugin uses the common plugin keywords as well as those for "sources".
|
||||
For more information check the 'plugins' topic for the former and the
|
||||
'sources' topic for the latter.
|
||||
|
||||
Additionally, this plugin uses the following plugin-specific keywords:
|
||||
|
||||
- node-packages:
|
||||
(list)
|
||||
A list of dependencies to fetch using npm.
|
||||
- node-engine:
|
||||
(string)
|
||||
The version of nodejs you want the snap to run on.
|
||||
- npm-run:
|
||||
(list)
|
||||
A list of targets to `npm run`.
|
||||
These targets will be run in order, after `npm install`
|
||||
- npm-flags:
|
||||
(list)
|
||||
A list of flags for npm.
|
||||
- node-package-manager
|
||||
(string; default: npm)
|
||||
The language package manager to use to drive installation
|
||||
of node packages. Can be either `npm` (default) or `yarn`.
|
||||
"""
|
||||
|
||||
import collections
|
||||
import contextlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import snapcraft
|
||||
from snapcraft import sources
|
||||
from snapcraft.file_utils import link_or_copy_tree
|
||||
from snapcraft.internal import errors
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_NODEJS_BASE = "node-v{version}-linux-{arch}"
|
||||
_NODEJS_VERSION = "12.18.4"
|
||||
_NODEJS_TMPL = "https://nodejs.org/dist/v{version}/{base}.tar.gz"
|
||||
_NODEJS_ARCHES = {"i386": "x86", "amd64": "x64", "armhf": "armv7l", "arm64": "arm64", "ppc64el": "ppc64le", "s390x": "s390x"}
|
||||
_YARN_URL = "https://yarnpkg.com/latest.tar.gz"
|
||||
|
||||
|
||||
class NodePlugin(snapcraft.BasePlugin):
|
||||
@classmethod
|
||||
def schema(cls):
|
||||
schema = super().schema()
|
||||
|
||||
schema["properties"]["node-packages"] = {
|
||||
"type": "array",
|
||||
"minitems": 1,
|
||||
"uniqueItems": True,
|
||||
"items": {"type": "string"},
|
||||
"default": [],
|
||||
}
|
||||
schema["properties"]["node-engine"] = {
|
||||
"type": "string",
|
||||
"default": _NODEJS_VERSION,
|
||||
}
|
||||
schema["properties"]["node-package-manager"] = {
|
||||
"type": "string",
|
||||
"default": "npm",
|
||||
"enum": ["npm", "yarn"],
|
||||
}
|
||||
schema["properties"]["npm-run"] = {
|
||||
"type": "array",
|
||||
"minitems": 1,
|
||||
"uniqueItems": False,
|
||||
"items": {"type": "string"},
|
||||
"default": [],
|
||||
}
|
||||
schema["properties"]["npm-flags"] = {
|
||||
"type": "array",
|
||||
"minitems": 1,
|
||||
"uniqueItems": False,
|
||||
"items": {"type": "string"},
|
||||
"default": [],
|
||||
}
|
||||
|
||||
if "required" in schema:
|
||||
del schema["required"]
|
||||
|
||||
return schema
|
||||
|
||||
@classmethod
|
||||
def get_build_properties(cls):
|
||||
# Inform Snapcraft of the properties associated with building. If these
|
||||
# change in the YAML Snapcraft will consider the build step dirty.
|
||||
return ["node-packages", "npm-run", "npm-flags"]
|
||||
|
||||
@classmethod
|
||||
def get_pull_properties(cls):
|
||||
# Inform Snapcraft of the properties associated with pulling. If these
|
||||
# change in the YAML Snapcraft will consider the build step dirty.
|
||||
return ["node-engine", "node-package-manager"]
|
||||
|
||||
@property
|
||||
def _nodejs_tar(self):
|
||||
if self._nodejs_tar_handle is None:
|
||||
self._nodejs_tar_handle = sources.Tar(
|
||||
self._nodejs_release_uri, self._npm_dir
|
||||
)
|
||||
return self._nodejs_tar_handle
|
||||
|
||||
@property
|
||||
def _yarn_tar(self):
|
||||
if self._yarn_tar_handle is None:
|
||||
self._yarn_tar_handle = sources.Tar(_YARN_URL, self._npm_dir)
|
||||
return self._yarn_tar_handle
|
||||
|
||||
def __init__(self, name, options, project):
|
||||
super().__init__(name, options, project)
|
||||
self._source_package_json = os.path.join(
|
||||
os.path.abspath(self.options.source), "package.json"
|
||||
)
|
||||
self._npm_dir = os.path.join(self.partdir, "npm")
|
||||
self._manifest = collections.OrderedDict()
|
||||
self._nodejs_release_uri = get_nodejs_release(
|
||||
self.options.node_engine, self.project.deb_arch
|
||||
)
|
||||
self._nodejs_tar_handle = None
|
||||
self._yarn_tar_handle = None
|
||||
|
||||
def pull(self):
|
||||
super().pull()
|
||||
os.makedirs(self._npm_dir, exist_ok=True)
|
||||
self._nodejs_tar.download()
|
||||
if self.options.node_package_manager == "yarn":
|
||||
self._yarn_tar.download()
|
||||
# do the install in the pull phase to download all dependencies.
|
||||
if self.options.node_package_manager == "npm":
|
||||
self._npm_install(rootdir=self.sourcedir)
|
||||
else:
|
||||
self._yarn_install(rootdir=self.sourcedir)
|
||||
|
||||
def clean_pull(self):
|
||||
super().clean_pull()
|
||||
|
||||
# Remove the npm directory (if any)
|
||||
if os.path.exists(self._npm_dir):
|
||||
shutil.rmtree(self._npm_dir)
|
||||
|
||||
def build(self):
|
||||
super().build()
|
||||
if self.options.node_package_manager == "npm":
|
||||
installed_node_packages = self._npm_install(rootdir=self.builddir)
|
||||
# Copy the content of the symlink to the build directory
|
||||
# LP: #1702661
|
||||
modules_dir = os.path.join(self.installdir, "lib", "node_modules")
|
||||
_copy_symlinked_content(modules_dir)
|
||||
else:
|
||||
installed_node_packages = self._yarn_install(rootdir=self.builddir)
|
||||
lock_file_path = os.path.join(self.sourcedir, "yarn.lock")
|
||||
if os.path.isfile(lock_file_path):
|
||||
with open(lock_file_path) as lock_file:
|
||||
self._manifest["yarn-lock-contents"] = lock_file.read()
|
||||
|
||||
self._manifest["node-packages"] = [
|
||||
"{}={}".format(name, installed_node_packages[name])
|
||||
for name in installed_node_packages
|
||||
]
|
||||
|
||||
def _npm_install(self, rootdir):
|
||||
self._nodejs_tar.provision(
|
||||
self.installdir, clean_target=False, keep_tarball=True
|
||||
)
|
||||
npm_cmd = ["npm"] + self.options.npm_flags
|
||||
npm_install = npm_cmd + ["--cache-min=Infinity", "install"]
|
||||
for pkg in self.options.node_packages:
|
||||
self.run(npm_install + ["--global"] + [pkg], cwd=rootdir)
|
||||
if os.path.exists(os.path.join(rootdir, "package.json")):
|
||||
self.run(npm_install, cwd=rootdir)
|
||||
self.run(npm_install + ["--global"], cwd=rootdir)
|
||||
for target in self.options.npm_run:
|
||||
self.run(npm_cmd + ["run", target], cwd=rootdir)
|
||||
return self._get_installed_node_packages("npm", self.installdir)
|
||||
|
||||
def _yarn_install(self, rootdir):
|
||||
self._nodejs_tar.provision(
|
||||
self.installdir, clean_target=False, keep_tarball=True
|
||||
)
|
||||
self._yarn_tar.provision(self._npm_dir, clean_target=False, keep_tarball=True)
|
||||
yarn_cmd = [os.path.join(self._npm_dir, "bin", "yarn")]
|
||||
yarn_cmd.extend(self.options.npm_flags)
|
||||
if "http_proxy" in os.environ:
|
||||
yarn_cmd.extend(["--proxy", os.environ["http_proxy"]])
|
||||
if "https_proxy" in os.environ:
|
||||
yarn_cmd.extend(["--https-proxy", os.environ["https_proxy"]])
|
||||
flags = []
|
||||
if rootdir == self.builddir:
|
||||
yarn_add = yarn_cmd + ["global", "add"]
|
||||
flags.extend(
|
||||
[
|
||||
"--offline",
|
||||
"--prod",
|
||||
"--global-folder",
|
||||
self.installdir,
|
||||
"--prefix",
|
||||
self.installdir,
|
||||
]
|
||||
)
|
||||
else:
|
||||
yarn_add = yarn_cmd + ["add"]
|
||||
for pkg in self.options.node_packages:
|
||||
self.run(yarn_add + [pkg] + flags, cwd=rootdir)
|
||||
|
||||
# local packages need to be added as if they were remote, we
|
||||
# remove the local package.json so `yarn add` doesn't pollute it.
|
||||
if os.path.exists(self._source_package_json):
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
os.unlink(os.path.join(rootdir, "package.json"))
|
||||
shutil.copy(
|
||||
self._source_package_json, os.path.join(rootdir, "package.json")
|
||||
)
|
||||
self.run(yarn_add + ["file:{}".format(rootdir)] + flags, cwd=rootdir)
|
||||
|
||||
# npm run would require to bring back package.json
|
||||
if self.options.npm_run and os.path.exists(self._source_package_json):
|
||||
# The current package.json is the yarn prefilled one.
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
os.unlink(os.path.join(rootdir, "package.json"))
|
||||
os.link(self._source_package_json, os.path.join(rootdir, "package.json"))
|
||||
for target in self.options.npm_run:
|
||||
self.run(
|
||||
yarn_cmd + ["run", target],
|
||||
cwd=rootdir,
|
||||
env=self._build_environment(rootdir),
|
||||
)
|
||||
return self._get_installed_node_packages("npm", self.installdir)
|
||||
|
||||
def _get_installed_node_packages(self, package_manager, cwd):
|
||||
try:
|
||||
output = self.run_output(
|
||||
[package_manager, "ls", "--global", "--json"], cwd=cwd
|
||||
)
|
||||
except subprocess.CalledProcessError as error:
|
||||
# XXX When dependencies have missing dependencies, an error like
|
||||
# this is printed to stderr:
|
||||
# npm ERR! peer dep missing: glob@*, required by glob-promise@3.1.0
|
||||
# retcode is not 0, which raises an exception.
|
||||
output = error.output.decode(sys.getfilesystemencoding()).strip()
|
||||
packages = collections.OrderedDict()
|
||||
dependencies = json.loads(output, object_pairs_hook=collections.OrderedDict)[
|
||||
"dependencies"
|
||||
]
|
||||
while dependencies:
|
||||
key, value = dependencies.popitem(last=False)
|
||||
# XXX Just as above, dependencies without version are the ones
|
||||
# missing.
|
||||
if "version" in value:
|
||||
packages[key] = value["version"]
|
||||
if "dependencies" in value:
|
||||
dependencies.update(value["dependencies"])
|
||||
return packages
|
||||
|
||||
def get_manifest(self):
|
||||
return self._manifest
|
||||
|
||||
def _build_environment(self, rootdir):
|
||||
env = os.environ.copy()
|
||||
if rootdir.endswith("src"):
|
||||
hidden_path = os.path.join(rootdir, "node_modules", ".bin")
|
||||
if env.get("PATH"):
|
||||
new_path = "{}:{}".format(hidden_path, env.get("PATH"))
|
||||
else:
|
||||
new_path = hidden_path
|
||||
env["PATH"] = new_path
|
||||
return env
|
||||
|
||||
|
||||
def _get_nodejs_base(node_engine, machine):
|
||||
if machine not in _NODEJS_ARCHES:
|
||||
raise errors.SnapcraftEnvironmentError(
|
||||
"architecture not supported ({})".format(machine)
|
||||
)
|
||||
return _NODEJS_BASE.format(version=node_engine, arch=_NODEJS_ARCHES[machine])
|
||||
|
||||
|
||||
def get_nodejs_release(node_engine, arch):
|
||||
return _NODEJS_TMPL.format(
|
||||
version=node_engine, base=_get_nodejs_base(node_engine, arch)
|
||||
)
|
||||
|
||||
|
||||
def _copy_symlinked_content(modules_dir):
|
||||
"""Copy symlinked content.
|
||||
|
||||
When running newer versions of npm, symlinks to the local tree are
|
||||
created from the part's installdir to the root of the builddir of the
|
||||
part (this only affects some build configurations in some projects)
|
||||
which is valid when running from the context of the part but invalid
|
||||
as soon as the artifacts migrate across the steps,
|
||||
i.e.; stage and prime.
|
||||
|
||||
If modules_dir does not exist we simply return.
|
||||
"""
|
||||
if not os.path.exists(modules_dir):
|
||||
return
|
||||
modules = [os.path.join(modules_dir, d) for d in os.listdir(modules_dir)]
|
||||
symlinks = [l for l in modules if os.path.islink(l)]
|
||||
for link_path in symlinks:
|
||||
link_target = os.path.realpath(link_path)
|
||||
os.unlink(link_path)
|
||||
link_or_copy_tree(link_target, link_path)
|
||||
+92
-25
@@ -1,5 +1,7 @@
|
||||
name: hugo
|
||||
version: git
|
||||
issues: https://github.com/gohugoio/hugo/issues
|
||||
source-code: https://github.com/gohugoio/hugo.git
|
||||
website: https://gohugo.io/
|
||||
summary: Fast and Flexible Static Site Generator
|
||||
description: |
|
||||
Hugo is a static HTML and CSS website generator written in Go. It is
|
||||
@@ -8,7 +10,7 @@ description: |
|
||||
license: "Apache-2.0"
|
||||
base: core20
|
||||
confinement: strict
|
||||
grade: devel # "devel" or "stable"
|
||||
adopt-info: hugo
|
||||
|
||||
package-repositories:
|
||||
- type: apt
|
||||
@@ -21,17 +23,26 @@ plugs:
|
||||
etc-gitconfig:
|
||||
interface: system-files
|
||||
read:
|
||||
- /etc/gitconfig
|
||||
- /etc/gitconfig
|
||||
gitconfig:
|
||||
interface: personal-files
|
||||
read:
|
||||
- $HOME/.gitconfig
|
||||
- $HOME/.config/git/config
|
||||
- $HOME/.gitconfig
|
||||
- $HOME/.config/git/config
|
||||
|
||||
environment:
|
||||
HOME: $SNAP_REAL_HOME
|
||||
GIT_EXEC_PATH: $SNAP/usr/lib/git-core
|
||||
GOCACHE: $SNAP_USER_DATA/.cache/go-build
|
||||
npm_config_cache: $SNAP_USER_DATA/.npm
|
||||
npm_config_init_module: $SNAP_USER_DATA/.npm-init.js
|
||||
npm_config_userconfig: $SNAP_USER_DATA/.npmrc
|
||||
pandoc_datadir: $SNAP/usr/share/pandoc
|
||||
PYTHONHOME: /usr:$SNAP/usr
|
||||
RUBYLIB: $SNAP/usr/lib/ruby/vendor_ruby/2.7.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ruby/vendor_ruby/2.7.0:$SNAP/usr/lib/ruby/vendor_ruby:$SNAP/usr/lib/ruby/2.7.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ruby/2.7.0
|
||||
|
||||
apps:
|
||||
hugo:
|
||||
environment:
|
||||
HOME: $SNAP_REAL_HOME
|
||||
command: bin/hugo
|
||||
completer: hugo-completion
|
||||
plugs:
|
||||
@@ -46,30 +57,58 @@ parts:
|
||||
plugin: nil
|
||||
stage-packages:
|
||||
- git
|
||||
organize:
|
||||
usr/bin/: bin/
|
||||
prime:
|
||||
- bin/git
|
||||
- usr/bin/git
|
||||
- usr/lib
|
||||
|
||||
go:
|
||||
plugin: nil
|
||||
stage-snaps:
|
||||
- go/1.19/stable
|
||||
prime:
|
||||
- bin/go
|
||||
- pkg/tool
|
||||
- -pkg/tool/*
|
||||
|
||||
hugo:
|
||||
plugin: nil
|
||||
build-snaps: [go/1.18/stable]
|
||||
source: .
|
||||
after:
|
||||
- git
|
||||
- go
|
||||
override-pull: |
|
||||
snapcraftctl pull
|
||||
snapcraftctl set-version "$(git describe --tags --always --match 'v[0-9]*' | sed 's/^v//; s/-/+git/; s/-g/./')"
|
||||
if grep -q 'Suffix:\s*""' common/hugo/version_current.go; then
|
||||
snapcraftctl set-grade "stable"
|
||||
else
|
||||
snapcraftctl set-grade "devel"
|
||||
fi
|
||||
override-build: |
|
||||
echo "\nStarting override-build:"
|
||||
set -ex
|
||||
|
||||
echo "\nStarting override-build:"
|
||||
export GOPATH=$(realpath ../go)
|
||||
export PATH=$GOPATH/bin:$PATH
|
||||
|
||||
echo " * Patch securityConfig.go to allow"
|
||||
echo " - GIT_EXEC_PATH and LD_LIBRARY_PATH to be passed to git"
|
||||
echo " - npm_config_{cache,init_module,userconfig} to be passed to npx"
|
||||
echo " - pandoc_datadir to be passed to pandoc"
|
||||
echo " - PYTHONHOME and SNAP to be passed to rst2html"
|
||||
echo " - RUBYLIB to be passed to asciidoctor"
|
||||
sed -i '/OsEnv: NewWhitelist/s/)\$/|GIT_EXEC_PATH|LD_LIBRARY_PATH|npm_config_(cache|init_module|userconfig)|pandoc_datadir|PYTHONHOME|RUBYLIB|SNAP&/' config/security/securityConfig.go
|
||||
git diff config/security/securityConfig.go
|
||||
|
||||
echo " * SNAPCRAFT_IMAGE_INFO=${SNAPCRAFT_IMAGE_INFO=}"
|
||||
# Example: SNAPCRAFT_IMAGE_INFO='{"build_url": "https://launchpad.net/~gohugoio/+snap/hugo-extended-dev/+build/344022"}'
|
||||
export HUGO_BUILD_TAGS=""
|
||||
if echo $SNAPCRAFT_IMAGE_INFO | grep -q '/+snap/hugo-extended'; then
|
||||
HUGO_BUILD_TAGS="extended"
|
||||
fi
|
||||
|
||||
echo " * Building hugo (HUGO_BUILD_TAGS=\"$HUGO_BUILD_TAGS\")..."
|
||||
go build -v -ldflags '-X github.com/gohugoio/hugo/common/hugo.vendorInfo=snap' -tags "$HUGO_BUILD_TAGS"
|
||||
go build -v -ldflags "-X github.com/gohugoio/hugo/common/hugo.vendorInfo=snap:$(git describe --tags --always --match 'v[0-9]*' | sed 's/^v//; s/-/+git/; s/-g/./')" -tags "$HUGO_BUILD_TAGS"
|
||||
./hugo version
|
||||
ldd hugo || :
|
||||
|
||||
@@ -86,25 +125,53 @@ parts:
|
||||
strip --remove-section=.comment --remove-section=.note $SNAPCRAFT_PART_INSTALL/bin/hugo
|
||||
ls -l $SNAPCRAFT_PART_INSTALL/bin/hugo
|
||||
|
||||
asciidoctor:
|
||||
plugin: nil
|
||||
stage-packages:
|
||||
- asciidoctor
|
||||
override-build: |
|
||||
set -ex
|
||||
snapcraftctl build
|
||||
sed -i '1s|#!/usr/bin/ruby|#!/usr/bin/env ruby|' $SNAPCRAFT_PART_INSTALL/usr/bin/asciidoctor
|
||||
|
||||
dart-sass-embedded:
|
||||
plugin: nil
|
||||
build-packages:
|
||||
- curl
|
||||
override-build: |
|
||||
set -ex
|
||||
snapcraftctl build
|
||||
case "$SNAPCRAFT_TARGET_ARCH" in
|
||||
amd64) arch=x64 ;;
|
||||
arm64) arch=arm64 ;;
|
||||
i386) arch=ia32 ;;
|
||||
*) arch="" ;;
|
||||
esac
|
||||
if [[ -n $arch ]]; then
|
||||
url=$(curl -s https://api.github.com/repos/sass/dart-sass-embedded/releases/latest | awk -F\" "/browser_download_url.*-linux-${arch}.tar.gz/{print \$(NF-1)}")
|
||||
curl -LO --retry-connrefused --retry 10 "$url"
|
||||
tar xf sass_embedded-*-linux-$arch.tar.gz sass_embedded/dart-sass-embedded
|
||||
install -d $SNAPCRAFT_PART_INSTALL/bin
|
||||
cp -av sass_embedded/dart-sass-embedded $SNAPCRAFT_PART_INSTALL/bin/
|
||||
fi
|
||||
|
||||
node:
|
||||
plugin: nil
|
||||
stage-packages:
|
||||
- nodejs
|
||||
organize:
|
||||
usr/bin/: bin/
|
||||
usr/lib/: lib/
|
||||
prime:
|
||||
- bin/node
|
||||
- lib/*/lib*.so*
|
||||
|
||||
pandoc:
|
||||
plugin: nil
|
||||
stage-packages:
|
||||
- libatomic1
|
||||
- pandoc
|
||||
- pandoc-data
|
||||
|
||||
rst2html:
|
||||
plugin: nil
|
||||
stage-packages:
|
||||
- python3-docutils
|
||||
override-build: |
|
||||
set -ex
|
||||
snapcraftctl build
|
||||
sed -i "s|'/usr/share/docutils/'|os.path.expandvars('\$SNAP/usr/share/docutils/')|" $SNAPCRAFT_PART_INSTALL/usr/lib/python3/dist-packages/docutils/__init__.py
|
||||
organize:
|
||||
usr/bin/: bin/
|
||||
usr/lib/: lib/
|
||||
prime:
|
||||
- bin/pandoc
|
||||
usr/share/docutils/scripts/python3: usr/bin
|
||||
|
||||
+12
-8
@@ -153,7 +153,7 @@ func newTemplateExec(d *deps.Deps) (*templateExec, error) {
|
||||
Deps: d,
|
||||
layoutHandler: output.NewLayoutHandler(),
|
||||
layoutsFs: d.BaseFs.Layouts.Fs,
|
||||
layoutTemplateCache: make(map[layoutCacheKey]tpl.Template),
|
||||
layoutTemplateCache: make(map[layoutCacheKey]layoutCacheEntry),
|
||||
|
||||
templateUsageTracker: templateUsageTracker,
|
||||
}
|
||||
@@ -328,7 +328,7 @@ type templateHandler struct {
|
||||
|
||||
layoutHandler *output.LayoutHandler
|
||||
|
||||
layoutTemplateCache map[layoutCacheKey]tpl.Template
|
||||
layoutTemplateCache map[layoutCacheKey]layoutCacheEntry
|
||||
layoutTemplateCacheMu sync.RWMutex
|
||||
|
||||
*deps.Deps
|
||||
@@ -357,6 +357,12 @@ type templateHandler struct {
|
||||
templateUsageTrackerMu sync.Mutex
|
||||
}
|
||||
|
||||
type layoutCacheEntry struct {
|
||||
found bool
|
||||
templ tpl.Template
|
||||
err error
|
||||
}
|
||||
|
||||
// AddTemplate parses and adds a template to the collection.
|
||||
// Templates with name prefixed with "_text" will be handled as plain
|
||||
// text templates.
|
||||
@@ -382,7 +388,7 @@ func (t *templateHandler) LookupLayout(d output.LayoutDescriptor, f output.Forma
|
||||
t.layoutTemplateCacheMu.RLock()
|
||||
if cacheVal, found := t.layoutTemplateCache[key]; found {
|
||||
t.layoutTemplateCacheMu.RUnlock()
|
||||
return cacheVal, true, nil
|
||||
return cacheVal.templ, cacheVal.found, cacheVal.err
|
||||
}
|
||||
t.layoutTemplateCacheMu.RUnlock()
|
||||
|
||||
@@ -390,12 +396,10 @@ func (t *templateHandler) LookupLayout(d output.LayoutDescriptor, f output.Forma
|
||||
defer t.layoutTemplateCacheMu.Unlock()
|
||||
|
||||
templ, found, err := t.findLayout(d, f)
|
||||
if err == nil && found {
|
||||
t.layoutTemplateCache[key] = templ
|
||||
return templ, true, nil
|
||||
}
|
||||
cacheVal := layoutCacheEntry{found: found, templ: templ, err: err}
|
||||
t.layoutTemplateCache[key] = cacheVal
|
||||
return cacheVal.templ, cacheVal.found, cacheVal.err
|
||||
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// This currently only applies to shortcodes and what we get here is the
|
||||
|
||||
@@ -180,7 +180,7 @@ a = "b"
|
||||
|
||||
converted, err := ns.Remarshal("toml", input)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(converted, qt.Equals, "[params]\n [params.variables]\n a = 'b'\n\n\n")
|
||||
c.Assert(converted, qt.Equals, "[params]\n [params.variables]\n a = 'b'\n")
|
||||
})
|
||||
|
||||
c.Run("Map input", func(c *qt.C) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user