Go 1.21 Upgrade

Fixes #11351
This commit is contained in:
Bjørn Erik Pedersen 2023-08-23 18:23:52 +02:00
parent 111f02db2a
commit 24b1be45c1
26 changed files with 438 additions and 108 deletions

View file

@ -4,7 +4,7 @@ parameters:
defaults: &defaults defaults: &defaults
resource_class: large resource_class: large
docker: docker:
- image: bepsays/ci-hugoreleaser:1.22000.20100 - image: bepsays/ci-hugoreleaser:1.22100.20000
environment: &buildenv environment: &buildenv
GOMODCACHE: /root/project/gomodcache GOMODCACHE: /root/project/gomodcache
version: 2 version: 2
@ -60,7 +60,7 @@ jobs:
environment: environment:
<<: [*buildenv] <<: [*buildenv]
docker: docker:
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22000.20100 - image: bepsays/ci-hugoreleaser-linux-arm64:1.22100.20000
steps: steps:
- *restore-cache - *restore-cache
- &attach-workspace - &attach-workspace

View file

@ -14,7 +14,7 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.20.x] go-version: [1.21.x]
os: [ubuntu-latest] os: [ubuntu-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

View file

@ -16,7 +16,7 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.19.x,1.20.x] go-version: [1.20.x,1.21.x]
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

View file

@ -396,8 +396,13 @@ func testSetupFunc() func(env *testscript.Env) error {
keyVals = append(keyVals, "SOURCE", sourceDir) keyVals = append(keyVals, "SOURCE", sourceDir)
goVersion := runtime.Version() goVersion := runtime.Version()
// Strip all but the major and minor version.
goVersion = regexp.MustCompile(`^go(\d+\.\d+)`).FindStringSubmatch(goVersion)[1] goVersion = strings.TrimPrefix(goVersion, "go")
if !strings.HasSuffix(goVersion, ".0") {
// Strip patch version.
goVersion = goVersion[:strings.LastIndex(goVersion, ".")]
}
keyVals = append(keyVals, "GOVERSION", goVersion) keyVals = append(keyVals, "GOVERSION", goVersion)
envhelpers.SetEnvVars(&env.Vars, keyVals...) envhelpers.SetEnvVars(&env.Vars, keyVals...)

View file

@ -16,7 +16,7 @@ import (
) )
func main() { func main() {
// The current is built with de4748c47c67392a57f250714509f590f68ad395 HEAD, tag: go1.20. // The current is built with c19c4c566c HEAD, tag: go1.21.0.
fmt.Println("Forking ...") fmt.Println("Forking ...")
defer fmt.Println("Done ...") defer fmt.Println("Done ...")

View file

@ -116,7 +116,7 @@ parts:
go: go:
plugin: nil plugin: nil
stage-snaps: stage-snaps:
- go/1.19/stable - go/1.21/stable
prime: prime:
- bin/go - bin/go
- pkg/tool - pkg/tool

View file

@ -38,6 +38,7 @@ const KnownEnv = `
GOARM GOARM
GOBIN GOBIN
GOCACHE GOCACHE
GOCACHEPROG
GOENV GOENV
GOEXE GOEXE
GOEXPERIMENT GOEXPERIMENT
@ -59,6 +60,7 @@ const KnownEnv = `
GOROOT GOROOT
GOSUMDB GOSUMDB
GOTMPDIR GOTMPDIR
GOTOOLCHAIN
GOTOOLDIR GOTOOLDIR
GOVCS GOVCS
GOWASM GOWASM

View file

@ -4,6 +4,18 @@ package template
import "strconv" import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[attrNone-0]
_ = x[attrScript-1]
_ = x[attrScriptType-2]
_ = x[attrStyle-3]
_ = x[attrURL-4]
_ = x[attrSrcset-5]
}
const _attr_name = "attrNoneattrScriptattrScriptTypeattrStyleattrURLattrSrcset" const _attr_name = "attrNoneattrScriptattrScriptTypeattrStyleattrURLattrSrcset"
var _attr_index = [...]uint8{0, 8, 18, 32, 41, 48, 58} var _attr_index = [...]uint8{0, 8, 18, 32, 41, 48, 58}

View file

@ -4,6 +4,16 @@ package template
import "strconv" import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[delimNone-0]
_ = x[delimDoubleQuote-1]
_ = x[delimSingleQuote-2]
_ = x[delimSpaceOrTagEnd-3]
}
const _delim_name = "delimNonedelimDoubleQuotedelimSingleQuotedelimSpaceOrTagEnd" const _delim_name = "delimNonedelimDoubleQuotedelimSingleQuotedelimSpaceOrTagEnd"
var _delim_index = [...]uint8{0, 9, 25, 41, 59} var _delim_index = [...]uint8{0, 9, 25, 41, 59}

View file

@ -5,16 +5,16 @@
/* /*
Package template (html/template) implements data-driven templates for Package template (html/template) implements data-driven templates for
generating HTML output safe against code injection. It provides the generating HTML output safe against code injection. It provides the
same interface as package text/template and should be used instead of same interface as [text/template] and should be used instead of
text/template whenever the output is HTML. [text/template] whenever the output is HTML.
The documentation here focuses on the security features of the package. The documentation here focuses on the security features of the package.
For information about how to program the templates themselves, see the For information about how to program the templates themselves, see the
documentation for text/template. documentation for [text/template].
# Introduction # Introduction
This package wraps package text/template so you can share its template API This package wraps [text/template] so you can share its template API
to parse and execute HTML templates safely. to parse and execute HTML templates safely.
tmpl, err := template.New("name").Parse(...) tmpl, err := template.New("name").Parse(...)

View file

@ -4,6 +4,17 @@ package template
import "strconv" import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[elementNone-0]
_ = x[elementScript-1]
_ = x[elementStyle-2]
_ = x[elementTextarea-3]
_ = x[elementTitle-4]
}
const _element_name = "elementNoneelementScriptelementStyleelementTextareaelementTitle" const _element_name = "elementNoneelementScriptelementStyleelementTextareaelementTitle"
var _element_index = [...]uint8{0, 11, 24, 36, 51, 63} var _element_index = [...]uint8{0, 11, 24, 36, 51, 63}

View file

@ -216,18 +216,13 @@ const (
// disallowed. Avoid using "html" and "urlquery" entirely in new templates. // disallowed. Avoid using "html" and "urlquery" entirely in new templates.
ErrPredefinedEscaper ErrPredefinedEscaper
// errJSTmplLit: "... appears in a JS template literal" // ErrJSTemplate: "... appears in a JS template literal"
// Example: // Example:
// <script>var tmpl = `{{.Interp}`</script> // <script>var tmpl = `{{.Interp}}`</script>
// Discussion: // Discussion:
// Package html/template does not support actions inside of JS template // Package html/template does not support actions inside of JS template
// literals. // literals.
// ErrJSTemplate
// TODO(rolandshoemaker): we cannot add this as an exported error in a minor
// release, since it is backwards incompatible with the other minor
// releases. As such we need to leave it unexported, and then we'll add it
// in the next major release.
errJSTmplLit
) )
func (e *Error) Error() string { func (e *Error) Error() string {

View file

@ -8,6 +8,8 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"html" "html"
//"internal/godebug"
"io" "io"
template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate" template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
@ -161,6 +163,7 @@ func (e *escaper) escape(c context, n parse.Node) context {
panic("escaping " + n.String() + " is unimplemented") panic("escaping " + n.String() + " is unimplemented")
} }
// Modified by Hugo.
// var debugAllowActionJSTmpl = godebug.New("jstmpllitinterp") // var debugAllowActionJSTmpl = godebug.New("jstmpllitinterp")
// escapeAction escapes an action template node. // escapeAction escapes an action template node.
@ -227,12 +230,13 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
case stateJSDqStr, stateJSSqStr: case stateJSDqStr, stateJSSqStr:
s = append(s, "_html_template_jsstrescaper") s = append(s, "_html_template_jsstrescaper")
case stateJSBqStr: case stateJSBqStr:
if SecurityAllowActionJSTmpl.Load() { // .Value() == "1" { if SecurityAllowActionJSTmpl.Load() {
//debugAllowActionJSTmpl.IncNonDefault()
s = append(s, "_html_template_jsstrescaper") s = append(s, "_html_template_jsstrescaper")
} else { } else {
return context{ return context{
state: stateError, state: stateError,
err: errorf(errJSTmplLit, n, n.Line, "%s appears in a JS template literal", n), err: errorf(ErrJSTemplate, n, n.Line, "%s appears in a JS template literal", n),
} }
} }
case stateJSRegexp: case stateJSRegexp:
@ -756,7 +760,7 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context {
} else if isComment(c.state) && c.delim == delimNone { } else if isComment(c.state) && c.delim == delimNone {
switch c.state { switch c.state {
case stateJSBlockCmt: case stateJSBlockCmt:
// https://es5.github.com/#x7.4: // https://es5.github.io/#x7.4:
// "Comments behave like white space and are // "Comments behave like white space and are
// discarded except that, if a MultiLineComment // discarded except that, if a MultiLineComment
// contains a line terminator character, then // contains a line terminator character, then

View file

@ -925,7 +925,7 @@ func TestJSEscaping(t *testing.T) {
{`'foo`, `\'foo`}, {`'foo`, `\'foo`},
{`Go "jump" \`, `Go \"jump\" \\`}, {`Go "jump" \`, `Go \"jump\" \\`},
{`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`},
{"unprintable \uFDFF", `unprintable \uFDFF`}, {"unprintable \uFFFE", `unprintable \uFFFE`},
{`<html>`, `\u003Chtml\u003E`}, {`<html>`, `\u003Chtml\u003E`},
{`no = in attributes`, `no \u003D in attributes`}, {`no = in attributes`, `no \u003D in attributes`},
{`&#x27; does not become HTML entity`, `\u0026#x27; does not become HTML entity`}, {`&#x27; does not become HTML entity`, `\u0026#x27; does not become HTML entity`},

View file

@ -397,7 +397,7 @@ func tLineCmt(c context, s []byte) (context, int) {
return c, len(s) return c, len(s)
} }
c.state = endState c.state = endState
// Per section 7.4 of EcmaScript 5 : https://es5.github.com/#x7.4 // Per section 7.4 of EcmaScript 5 : https://es5.github.io/#x7.4
// "However, the LineTerminator at the end of the line is not // "However, the LineTerminator at the end of the line is not
// considered to be part of the single-line comment; it is // considered to be part of the single-line comment; it is
// recognized separately by the lexical grammar and becomes part // recognized separately by the lexical grammar and becomes part

View file

@ -4,6 +4,16 @@ package template
import "strconv" import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[urlPartNone-0]
_ = x[urlPartPreQuery-1]
_ = x[urlPartQueryOrFrag-2]
_ = x[urlPartUnknown-3]
}
const _urlPart_name = "urlPartNoneurlPartPreQueryurlPartQueryOrFragurlPartUnknown" const _urlPart_name = "urlPartNoneurlPartPreQueryurlPartQueryOrFragurlPartUnknown"
var _urlPart_index = [...]uint8{0, 11, 26, 44, 58} var _urlPart_index = [...]uint8{0, 11, 26, 44, 58}

View file

@ -6,33 +6,78 @@ package testenv
import ( import (
"context" "context"
"fmt"
"os" "os"
"os/exec" "os/exec"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync" "sync"
"testing" "testing"
"time"
) )
// HasExec reports whether the current system can start new processes
// using os.StartProcess or (more commonly) exec.Command.
func HasExec() bool {
switch runtime.GOOS {
case "js", "ios":
return false
}
return true
}
// MustHaveExec checks that the current system can start new processes // MustHaveExec checks that the current system can start new processes
// using os.StartProcess or (more commonly) exec.Command. // using os.StartProcess or (more commonly) exec.Command.
// If not, MustHaveExec calls t.Skip with an explanation. // If not, MustHaveExec calls t.Skip with an explanation.
//
// On some platforms MustHaveExec checks for exec support by re-executing the
// current executable, which must be a binary built by 'go test'.
// We intentionally do not provide a HasExec function because of the risk of
// inappropriate recursion in TestMain functions.
//
// To check for exec support outside of a test, just try to exec the command.
// If exec is not supported, testenv.SyscallIsNotSupported will return true
// for the resulting error.
func MustHaveExec(t testing.TB) { func MustHaveExec(t testing.TB) {
if !HasExec() { tryExecOnce.Do(func() {
t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH) tryExecErr = tryExec()
})
if tryExecErr != nil {
t.Skipf("skipping test: cannot exec subprocess on %s/%s: %v", runtime.GOOS, runtime.GOARCH, tryExecErr)
} }
} }
var (
tryExecOnce sync.Once
tryExecErr error
)
func tryExec() error {
switch runtime.GOOS {
case "wasip1", "js", "ios":
default:
// Assume that exec always works on non-mobile platforms and Android.
return nil
}
// ios has an exec syscall but on real iOS devices it might return a
// permission error. In an emulated environment (such as a Corellium host)
// it might succeed, so if we need to exec we'll just have to try it and
// find out.
//
// As of 2023-04-19 wasip1 and js don't have exec syscalls at all, but we
// may as well use the same path so that this branch can be tested without
// an ios environment.
/*if !testing.Testing() {
// This isn't a standard 'go test' binary, so we don't know how to
// self-exec in a way that should succeed without side effects.
// Just forget it.
return errors.New("can't probe for exec support with a non-test executable")
}*/
// We know that this is a test executable. We should be able to run it with a
// no-op flag to check for overall exec support.
exe, err := os.Executable()
if err != nil {
return fmt.Errorf("can't probe for exec support: %w", err)
}
cmd := exec.Command(exe, "-test.list=^$")
cmd.Env = origEnv
return cmd.Run()
}
var execPaths sync.Map // path -> error var execPaths sync.Map // path -> error
// MustHaveExecPath checks that the current system can start the named executable // MustHaveExecPath checks that the current system can start the named executable
@ -82,7 +127,87 @@ func CleanCmdEnv(cmd *exec.Cmd) *exec.Cmd {
// - fails the test if the command does not complete before the test's deadline, and // - fails the test if the command does not complete before the test's deadline, and
// - sets a Cleanup function that verifies that the test did not leak a subprocess. // - sets a Cleanup function that verifies that the test did not leak a subprocess.
func CommandContext(t testing.TB, ctx context.Context, name string, args ...string) *exec.Cmd { func CommandContext(t testing.TB, ctx context.Context, name string, args ...string) *exec.Cmd {
panic("Not implemented, Hugo is not using this") t.Helper()
MustHaveExec(t)
var (
cancelCtx context.CancelFunc
gracePeriod time.Duration // unlimited unless the test has a deadline (to allow for interactive debugging)
)
if t, ok := t.(interface {
testing.TB
Deadline() (time.Time, bool)
}); ok {
if td, ok := t.Deadline(); ok {
// Start with a minimum grace period, just long enough to consume the
// output of a reasonable program after it terminates.
gracePeriod = 100 * time.Millisecond
if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
scale, err := strconv.Atoi(s)
if err != nil {
t.Fatalf("invalid GO_TEST_TIMEOUT_SCALE: %v", err)
}
gracePeriod *= time.Duration(scale)
}
// If time allows, increase the termination grace period to 5% of the
// test's remaining time.
testTimeout := time.Until(td)
if gp := testTimeout / 20; gp > gracePeriod {
gracePeriod = gp
}
// When we run commands that execute subprocesses, we want to reserve two
// grace periods to clean up: one for the delay between the first
// termination signal being sent (via the Cancel callback when the Context
// expires) and the process being forcibly terminated (via the WaitDelay
// field), and a second one for the delay between the process being
// terminated and the test logging its output for debugging.
//
// (We want to ensure that the test process itself has enough time to
// log the output before it is also terminated.)
cmdTimeout := testTimeout - 2*gracePeriod
if cd, ok := ctx.Deadline(); !ok || time.Until(cd) > cmdTimeout {
// Either ctx doesn't have a deadline, or its deadline would expire
// after (or too close before) the test has already timed out.
// Add a shorter timeout so that the test will produce useful output.
ctx, cancelCtx = context.WithTimeout(ctx, cmdTimeout)
}
}
}
cmd := exec.CommandContext(ctx, name, args...)
cmd.Cancel = func() error {
if cancelCtx != nil && ctx.Err() == context.DeadlineExceeded {
// The command timed out due to running too close to the test's deadline.
// There is no way the test did that intentionally — it's too close to the
// wire! — so mark it as a test failure. That way, if the test expects the
// command to fail for some other reason, it doesn't have to distinguish
// between that reason and a timeout.
t.Errorf("test timed out while running command: %v", cmd)
} else {
// The command is being terminated due to ctx being canceled, but
// apparently not due to an explicit test deadline that we added.
// Log that information in case it is useful for diagnosing a failure,
// but don't actually fail the test because of it.
t.Logf("%v: terminating command: %v", ctx.Err(), cmd)
}
return cmd.Process.Signal(Sigquit)
}
cmd.WaitDelay = gracePeriod
t.Cleanup(func() {
if cancelCtx != nil {
cancelCtx()
}
if cmd.Process != nil && cmd.ProcessState == nil {
t.Errorf("command was started, but test did not wait for it to complete: %v", cmd)
}
})
return cmd
} }
// Command is like exec.Command, but applies the same changes as // Command is like exec.Command, but applies the same changes as

View file

@ -11,9 +11,14 @@
package testenv package testenv
import ( import (
"bytes"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"
//"internal/platform"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -22,10 +27,14 @@ import (
"strings" "strings"
"sync" "sync"
"testing" "testing"
"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"
) )
// Save the original environment during init for use in checks. A test
// binary may modify its environment before calling HasExec to change its
// behavior (such as mimicking a command-line tool), and that modified
// environment might cause environment checks to behave erratically.
var origEnv = os.Environ()
// Builder reports the name of the builder running this test // Builder reports the name of the builder running this test
// (for example, "linux-amd64" or "windows-386-gce"). // (for example, "linux-amd64" or "windows-386-gce").
// If the test is not running on the build infrastructure, // If the test is not running on the build infrastructure,
@ -37,29 +46,26 @@ func Builder() string {
// HasGoBuild reports whether the current system can build programs with “go build” // HasGoBuild reports whether the current system can build programs with “go build”
// and then run them with os.StartProcess or exec.Command. // and then run them with os.StartProcess or exec.Command.
func HasGoBuild() bool { func HasGoBuild() bool {
if os.Getenv("GO_GCFLAGS") != "" { // Modified by Hugo (not needed)
// It's too much work to require every caller of the go command
// to pass along "-gcflags="+os.Getenv("GO_GCFLAGS").
// For now, if $GO_GCFLAGS is set, report that we simply can't
// run go build.
return false return false
}
switch runtime.GOOS {
case "android", "js", "ios":
return false
}
return true
} }
var (
goBuildOnce sync.Once
goBuildErr error
)
// MustHaveGoBuild checks that the current system can build programs with “go build” // MustHaveGoBuild checks that the current system can build programs with “go build”
// and then run them with os.StartProcess or exec.Command. // and then run them with os.StartProcess or exec.Command.
// If not, MustHaveGoBuild calls t.Skip with an explanation. // If not, MustHaveGoBuild calls t.Skip with an explanation.
func MustHaveGoBuild(t testing.TB) { func MustHaveGoBuild(t testing.TB) {
if os.Getenv("GO_GCFLAGS") != "" { if os.Getenv("GO_GCFLAGS") != "" {
t.Helper()
t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS") t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS")
} }
if !HasGoBuild() { if !HasGoBuild() {
t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH) t.Helper()
t.Skipf("skipping test: 'go build' unavailable: %v", goBuildErr)
} }
} }
@ -77,6 +83,25 @@ func MustHaveGoRun(t testing.TB) {
} }
} }
// HasParallelism reports whether the current system can execute multiple
// threads in parallel.
// There is a copy of this function in cmd/dist/test.go.
func HasParallelism() bool {
switch runtime.GOOS {
case "js", "wasip1":
return false
}
return true
}
// MustHaveParallelism checks that the current system can execute multiple
// threads in parallel. If not, MustHaveParallelism calls t.Skip with an explanation.
func MustHaveParallelism(t testing.TB) {
if !HasParallelism() {
t.Skipf("skipping test: no parallelism available on %s/%s", runtime.GOOS, runtime.GOARCH)
}
}
// GoToolPath reports the path to the Go tool. // GoToolPath reports the path to the Go tool.
// It is a convenience wrapper around GoTool. // It is a convenience wrapper around GoTool.
// If the tool is unavailable GoToolPath calls t.Skip. // If the tool is unavailable GoToolPath calls t.Skip.
@ -124,6 +149,9 @@ func findGOROOT() (string, error) {
// runs the test in the directory containing the packaged under test.) That // runs the test in the directory containing the packaged under test.) That
// means that if we start walking up the tree, we should eventually find // means that if we start walking up the tree, we should eventually find
// GOROOT/src/go.mod, and we can report the parent directory of that. // GOROOT/src/go.mod, and we can report the parent directory of that.
//
// Notably, this works even if we can't run 'go env GOROOT' as a
// subprocess.
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
@ -174,7 +202,8 @@ func findGOROOT() (string, error) {
// GOROOT reports the path to the directory containing the root of the Go // GOROOT reports the path to the directory containing the root of the Go
// project source tree. This is normally equivalent to runtime.GOROOT, but // project source tree. This is normally equivalent to runtime.GOROOT, but
// works even if the test binary was built with -trimpath. // works even if the test binary was built with -trimpath and cannot exec
// 'go env GOROOT'.
// //
// If GOROOT cannot be found, GOROOT skips t if t is non-nil, // If GOROOT cannot be found, GOROOT skips t if t is non-nil,
// or panics otherwise. // or panics otherwise.
@ -195,25 +224,18 @@ func GoTool() (string, error) {
if !HasGoBuild() { if !HasGoBuild() {
return "", errors.New("platform cannot run go tool") return "", errors.New("platform cannot run go tool")
} }
var exeSuffix string goToolOnce.Do(func() {
if runtime.GOOS == "windows" { goToolPath, goToolErr = exec.LookPath("go")
exeSuffix = ".exe" })
} return goToolPath, goToolErr
goroot, err := findGOROOT()
if err != nil {
return "", fmt.Errorf("cannot find go tool: %w", err)
}
path := filepath.Join(goroot, "bin", "go"+exeSuffix)
if _, err := os.Stat(path); err == nil {
return path, nil
}
goBin, err := exec.LookPath("go" + exeSuffix)
if err != nil {
return "", errors.New("cannot find go tool: " + err.Error())
}
return goBin, nil
} }
var (
goToolOnce sync.Once
goToolPath string
goToolErr error
)
// HasSrc reports whether the entire source tree is available under GOROOT. // HasSrc reports whether the entire source tree is available under GOROOT.
func HasSrc() bool { func HasSrc() bool {
switch runtime.GOOS { switch runtime.GOOS {
@ -226,50 +248,82 @@ func HasSrc() bool {
// HasExternalNetwork reports whether the current system can use // HasExternalNetwork reports whether the current system can use
// external (non-localhost) networks. // external (non-localhost) networks.
func HasExternalNetwork() bool { func HasExternalNetwork() bool {
return !testing.Short() && runtime.GOOS != "js" return !testing.Short() && runtime.GOOS != "js" && runtime.GOOS != "wasip1"
} }
// MustHaveExternalNetwork checks that the current system can use // MustHaveExternalNetwork checks that the current system can use
// external (non-localhost) networks. // external (non-localhost) networks.
// If not, MustHaveExternalNetwork calls t.Skip with an explanation. // If not, MustHaveExternalNetwork calls t.Skip with an explanation.
func MustHaveExternalNetwork(t testing.TB) { func MustHaveExternalNetwork(t testing.TB) {
if runtime.GOOS == "js" { if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
t.Helper()
t.Skipf("skipping test: no external network on %s", runtime.GOOS) t.Skipf("skipping test: no external network on %s", runtime.GOOS)
} }
if testing.Short() { if testing.Short() {
t.Helper()
t.Skipf("skipping test: no external network in -short mode") t.Skipf("skipping test: no external network in -short mode")
} }
} }
var haveCGO bool
// HasCGO reports whether the current system can use cgo. // HasCGO reports whether the current system can use cgo.
func HasCGO() bool { func HasCGO() bool {
return haveCGO hasCgoOnce.Do(func() {
goTool, err := GoTool()
if err != nil {
return
}
cmd := exec.Command(goTool, "env", "CGO_ENABLED")
cmd.Env = origEnv
out, err := cmd.Output()
if err != nil {
panic(fmt.Sprintf("%v: %v", cmd, out))
}
hasCgo, err = strconv.ParseBool(string(bytes.TrimSpace(out)))
if err != nil {
panic(fmt.Sprintf("%v: non-boolean output %q", cmd, out))
}
})
return hasCgo
} }
var (
hasCgoOnce sync.Once
hasCgo bool
)
// MustHaveCGO calls t.Skip if cgo is not available. // MustHaveCGO calls t.Skip if cgo is not available.
func MustHaveCGO(t testing.TB) { func MustHaveCGO(t testing.TB) {
if !haveCGO { if !HasCGO() {
t.Skipf("skipping test: no cgo") t.Skipf("skipping test: no cgo")
} }
} }
// CanInternalLink reports whether the current system can link programs with // CanInternalLink reports whether the current system can link programs with
// internal linking. // internal linking.
func CanInternalLink() bool { func CanInternalLink(withCgo bool) bool {
panic("not implemented, not needed by Hugo") // Modified by Hugo (not needed)
return false
} }
// MustInternalLink checks that the current system can link programs with internal // MustInternalLink checks that the current system can link programs with internal
// linking. // linking.
// If not, MustInternalLink calls t.Skip with an explanation. // If not, MustInternalLink calls t.Skip with an explanation.
func MustInternalLink(t testing.TB) { func MustInternalLink(t testing.TB, withCgo bool) {
if !CanInternalLink() { if !CanInternalLink(withCgo) {
if withCgo && CanInternalLink(false) {
t.Skipf("skipping test: internal linking on %s/%s is not supported with cgo", runtime.GOOS, runtime.GOARCH)
}
t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH) t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH)
} }
} }
// MustHaveBuildMode reports whether the current system can build programs in
// the given build mode.
// If not, MustHaveBuildMode calls t.Skip with an explanation.
func MustHaveBuildMode(t testing.TB, buildmode string) {
// Modified by Hugo (not needed)
}
// HasSymlink reports whether the current system can use os.Symlink. // HasSymlink reports whether the current system can use os.Symlink.
func HasSymlink() bool { func HasSymlink() bool {
ok, _ := hasSymlink() ok, _ := hasSymlink()
@ -281,7 +335,7 @@ func HasSymlink() bool {
func MustHaveSymlink(t testing.TB) { func MustHaveSymlink(t testing.TB) {
ok, reason := hasSymlink() ok, reason := hasSymlink()
if !ok { if !ok {
t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason) t.Skipf("skipping test: cannot make symlinks on %s/%s: %s", runtime.GOOS, runtime.GOARCH, reason)
} }
} }
@ -320,7 +374,7 @@ func SkipFlakyNet(t testing.TB) {
// CPUIsSlow reports whether the CPU running the test is suspected to be slow. // CPUIsSlow reports whether the CPU running the test is suspected to be slow.
func CPUIsSlow() bool { func CPUIsSlow() bool {
switch runtime.GOARCH { switch runtime.GOARCH {
case "arm", "mips", "mipsle", "mips64", "mips64le": case "arm", "mips", "mipsle", "mips64", "mips64le", "wasm":
return true return true
} }
return false return false
@ -346,8 +400,51 @@ func SkipIfOptimizationOff(t testing.TB) {
} }
// WriteImportcfg writes an importcfg file used by the compiler or linker to // WriteImportcfg writes an importcfg file used by the compiler or linker to
// dstPath containing entries for the packages in std and cmd in addition // dstPath containing entries for the file mappings in packageFiles, as well
// to the package to package file mappings in additionalPackageFiles. // as for the packages transitively imported by the package(s) in pkgs.
func WriteImportcfg(t testing.TB, dstPath string, additionalPackageFiles map[string]string) { //
panic("not implemented, not needed by Hugo") // pkgs may include any package pattern that is valid to pass to 'go list',
// so it may also be a list of Go source files all in the same directory.
func WriteImportcfg(t testing.TB, dstPath string, packageFiles map[string]string, pkgs ...string) {
t.Helper()
icfg := new(bytes.Buffer)
icfg.WriteString("# import config\n")
for k, v := range packageFiles {
fmt.Fprintf(icfg, "packagefile %s=%s\n", k, v)
}
if len(pkgs) > 0 {
// Use 'go list' to resolve any missing packages and rewrite the import map.
cmd := Command(t, GoToolPath(t), "list", "-export", "-deps", "-f", `{{if ne .ImportPath "command-line-arguments"}}{{if .Export}}{{.ImportPath}}={{.Export}}{{end}}{{end}}`)
cmd.Args = append(cmd.Args, pkgs...)
cmd.Stderr = new(strings.Builder)
out, err := cmd.Output()
if err != nil {
t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr)
}
for _, line := range strings.Split(string(out), "\n") {
if line == "" {
continue
}
importPath, export, ok := strings.Cut(line, "=")
if !ok {
t.Fatalf("invalid line in output from %v:\n%s", cmd, line)
}
if packageFiles[importPath] == "" {
fmt.Fprintf(icfg, "packagefile %s=%s\n", importPath, export)
}
}
}
if err := os.WriteFile(dstPath, icfg.Bytes(), 0666); err != nil {
t.Fatal(err)
}
}
// SyscallIsNotSupported reports whether err may indicate that a system call is
// not supported by the current platform or execution environment.
func SyscallIsNotSupported(err error) bool {
return syscallIsNotSupported(err)
} }

View file

@ -1,11 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
package testenv
func init() {
haveCGO = true
}

View file

@ -2,12 +2,19 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build windows || plan9 || (js && wasm) //go:build windows || plan9 || (js && wasm) || wasip1
package testenv package testenv
import "os" import (
"os"
)
// Sigquit is the signal to send to kill a hanging subprocess. // Sigquit is the signal to send to kill a hanging subprocess.
// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill. // On Unix we send SIGQUIT, but on non-Unix we only have os.Kill.
var Sigquit = os.Kill var Sigquit = os.Kill
func syscallIsNotSupported(err error) bool {
// Removed by Hugo (not supported in Go 1.20).
return false
}

View file

@ -7,13 +7,39 @@
package testenv package testenv
import ( import (
"fmt"
"os"
"path/filepath"
"runtime" "runtime"
) )
func hasSymlink() (ok bool, reason string) { func hasSymlink() (ok bool, reason string) {
switch runtime.GOOS { switch runtime.GOOS {
case "android", "plan9": case "plan9":
return false, "" return false, ""
case "android", "wasip1":
// For wasip1, some runtimes forbid absolute symlinks,
// or symlinks that escape the current working directory.
// Perform a simple test to see whether the runtime
// supports symlinks or not. If we get a permission
// error, the runtime does not support symlinks.
dir, err := os.MkdirTemp("", "")
if err != nil {
return false, ""
}
defer func() {
_ = os.RemoveAll(dir)
}()
fpath := filepath.Join(dir, "testfile.txt")
if err := os.WriteFile(fpath, nil, 0644); err != nil {
return false, ""
}
if err := os.Symlink(fpath, filepath.Join(dir, "testlink")); err != nil {
if SyscallIsNotSupported(err) {
return false, fmt.Sprintf("symlinks unsupported: %s", err.Error())
}
return false, ""
}
} }
return true, "" return true, ""

View file

@ -5,16 +5,16 @@
package testenv_test package testenv_test
import ( import (
"github.com/gohugoio/hugo/tpl/internal/go_templates/testenv"
//"internal/platform"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings"
"testing" "testing"
"github.com/gohugoio/hugo/tpl/internal/go_templates/testenv"
) )
func TestGoToolLocation(t *testing.T) { func TestGoToolLocation(t *testing.T) {
t.Skip("skipping test that requires go command")
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
var exeSuffix string var exeSuffix string
@ -53,3 +53,33 @@ func TestGoToolLocation(t *testing.T) {
t.Fatalf("%q is not the same file as %q", absWant, goTool) t.Fatalf("%q is not the same file as %q", absWant, goTool)
} }
} }
func TestHasGoBuild(t *testing.T) {
// Removed by Hugo.
}
func TestMustHaveExec(t *testing.T) {
hasExec := false
t.Run("MustHaveExec", func(t *testing.T) {
testenv.MustHaveExec(t)
t.Logf("MustHaveExec did not skip")
hasExec = true
})
switch runtime.GOOS {
case "js", "wasip1":
if hasExec {
// js and wasip1 lack an “exec” syscall.
t.Errorf("expected MustHaveExec to skip on %v", runtime.GOOS)
}
case "ios":
if b := testenv.Builder(); strings.HasSuffix(b, "-corellium") && !hasExec {
// Most ios environments can't exec, but the corellium builder can.
t.Errorf("expected MustHaveExec not to skip on %v", b)
}
default:
if b := testenv.Builder(); b != "" && !hasExec {
t.Errorf("expected MustHaveExec not to skip on %v", b)
}
}
}

View file

@ -6,8 +6,15 @@
package testenv package testenv
import "syscall" import (
"syscall"
)
// Sigquit is the signal to send to kill a hanging subprocess. // Sigquit is the signal to send to kill a hanging subprocess.
// Send SIGQUIT to get a stack trace. // Send SIGQUIT to get a stack trace.
var Sigquit = syscall.SIGQUIT var Sigquit = syscall.SIGQUIT
func syscallIsNotSupported(err error) bool {
// Removed by Hugo (not supported in Go 1.20)
return false
}

View file

@ -5,7 +5,7 @@
/* /*
Package template implements data-driven templates for generating textual output. Package template implements data-driven templates for generating textual output.
To generate HTML output, see package html/template, which has the same interface To generate HTML output, see [html/template], which has the same interface
as this package but automatically secures HTML output against certain attacks. as this package but automatically secures HTML output against certain attacks.
Templates are executed by applying them to a data structure. Annotations in the Templates are executed by applying them to a data structure. Annotations in the

View file

@ -953,7 +953,7 @@ func TestJSEscaping(t *testing.T) {
{`'foo`, `\'foo`}, {`'foo`, `\'foo`},
{`Go "jump" \`, `Go \"jump\" \\`}, {`Go "jump" \`, `Go \"jump\" \\`},
{`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`},
{"unprintable \uFDFF", `unprintable \uFDFF`}, {"unprintable \uFFFE", `unprintable \uFFFE`},
{`<html>`, `\u003Chtml\u003E`}, {`<html>`, `\u003Chtml\u003E`},
{`no = in attributes`, `no \u003D in attributes`}, {`no = in attributes`, `no \u003D in attributes`},
{`&#x27; does not become HTML entity`, `\u0026#x27; does not become HTML entity`}, {`&#x27; does not become HTML entity`, `\u0026#x27; does not become HTML entity`},

View file

@ -23,7 +23,7 @@ import (
// Execute returns that error. // Execute returns that error.
// //
// Errors returned by Execute wrap the underlying error; call errors.As to // Errors returned by Execute wrap the underlying error; call errors.As to
// uncover them. // unwrap them.
// //
// When template execution invokes a function with an argument list, that list // When template execution invokes a function with an argument list, that list
// must be assignable to the function's parameter types. Functions meant to // must be assignable to the function's parameter types. Functions meant to