// Copyright 2019 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 modules
import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
hglob "github.com/gohugoio/hugo/hugofs/glob"
"github.com/gobwas/glob"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/config"
"github.com/rogpeppe/go-internal/module"
"github.com/gohugoio/hugo/common/hugio"
"github.com/spf13/afero"
)
var fileSeparator = string(os.PathSeparator)
const (
goBinaryStatusOK goBinaryStatus = iota
goBinaryStatusNotFound
goBinaryStatusTooOld
)
// The "vendor" dir is reserved for Go Modules.
const vendord = "_vendor"
const (
goModFilename = "go.mod"
goSumFilename = "go.sum"
)
// NewClient creates a new Client that can be used to manage the Hugo Components
// in a given workingDir.
// The Client will resolve the dependencies recursively, but needs the top
// level imports to start out.
func NewClient(cfg ClientConfig) *Client {
fs := cfg.Fs
n := filepath.Join(cfg.WorkingDir, goModFilename)
goModEnabled, _ := afero.Exists(fs, n)
var goModFilename string
if goModEnabled {
goModFilename = n
}
var env []string
mcfg := cfg.ModuleConfig
config.SetEnvVars(&env,
"PWD", cfg.WorkingDir,
"GO111MODULE", "on",
"GOPROXY", mcfg.Proxy,
"GOPRIVATE", mcfg.Private,
"GONOPROXY", mcfg.NoProxy,
"GOPATH", cfg.CacheDir,
"GOWORK", mcfg.Workspace, // Requires Go 1.18, see https://tip.golang.org/doc/go1.18
// GOCACHE was introduced in Go 1.15. This matches the location derived from GOPATH above.
"GOCACHE", filepath.Join(cfg.CacheDir, "pkg", "mod"),
)
logger := cfg.Logger
if logger == nil {
logger = loggers.NewDefault()
}
var noVendor glob.Glob
if cfg.ModuleConfig.NoVendor != "" {
noVendor, _ = hglob.GetGlob(hglob.NormalizePath(cfg.ModuleConfig.NoVendor))
}
var throttleSince time.Time
throttle := func(f func()) {
if throttleSince.IsZero() {
throttleSince = time.Now()
f()
return
}
if time.Since(throttleSince) < 6*time.Second {
return
}
throttleSince = time.Now()
f()
}
return &Client{
fs: fs,
ccfg: cfg,
logger: logger,
throttle: throttle,
noVendor: noVendor,
moduleConfig: mcfg,
environ: env,
GoModulesFilename: goModFilename,
}
}
// Client contains most of the API provided by this package.
type Client struct {
fs