2018-12-20 14:22:03 -05:00
|
|
|
// 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 deploy
|
|
|
|
|
|
|
|
import (
|
2024-01-28 16:34:10 -05:00
|
|
|
"errors"
|
2018-12-20 14:22:03 -05:00
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
|
2020-02-27 01:26:05 -05:00
|
|
|
"github.com/gobwas/glob"
|
2018-12-20 14:22:03 -05:00
|
|
|
"github.com/gohugoio/hugo/config"
|
2020-02-27 01:26:05 -05:00
|
|
|
hglob "github.com/gohugoio/hugo/hugofs/glob"
|
2018-12-20 14:22:03 -05:00
|
|
|
"github.com/mitchellh/mapstructure"
|
|
|
|
)
|
|
|
|
|
|
|
|
const deploymentConfigKey = "deployment"
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
// DeployConfig is the complete configuration for deployment.
|
|
|
|
type DeployConfig struct {
|
|
|
|
Targets []*Target
|
|
|
|
Matchers []*Matcher
|
2019-05-03 12:30:46 -04:00
|
|
|
Order []string
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
// Usually set via flags.
|
|
|
|
// Target deployment Name; defaults to the first one.
|
|
|
|
Target string
|
|
|
|
// Show a confirm prompt before deploying.
|
|
|
|
Confirm bool
|
|
|
|
// DryRun will try the deployment without any remote changes.
|
|
|
|
DryRun bool
|
|
|
|
// Force will re-upload all files.
|
|
|
|
Force bool
|
|
|
|
// Invalidate the CDN cache listed in the deployment target.
|
|
|
|
InvalidateCDN bool
|
|
|
|
// MaxDeletes is the maximum number of files to delete.
|
|
|
|
MaxDeletes int
|
|
|
|
// Number of concurrent workers to use when uploading files.
|
|
|
|
Workers int
|
|
|
|
|
|
|
|
ordering []*regexp.Regexp // compiled Order
|
2018-12-20 14:22:03 -05:00
|
|
|
}
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
type Target struct {
|
2018-12-20 14:22:03 -05:00
|
|
|
Name string
|
|
|
|
URL string
|
2019-05-01 16:25:06 -04:00
|
|
|
|
|
|
|
CloudFrontDistributionID string
|
2019-10-03 08:46:27 -04:00
|
|
|
|
|
|
|
// GoogleCloudCDNOrigin specifies the Google Cloud project and CDN origin to
|
|
|
|
// invalidate when deploying this target. It is specified as <project>/<origin>.
|
|
|
|
GoogleCloudCDNOrigin string
|
2020-02-27 01:26:05 -05:00
|
|
|
|
|
|
|
// Optional patterns of files to include/exclude for this target.
|
|
|
|
// Parsed using github.com/gobwas/glob.
|
|
|
|
Include string
|
|
|
|
Exclude string
|
|
|
|
|
|
|
|
// Parsed versions of Include/Exclude.
|
|
|
|
includeGlob glob.Glob
|
|
|
|
excludeGlob glob.Glob
|
|
|
|
}
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
func (tgt *Target) parseIncludeExclude() error {
|
2020-02-27 01:26:05 -05:00
|
|
|
var err error
|
|
|
|
if tgt.Include != "" {
|
|
|
|
tgt.includeGlob, err = hglob.GetGlob(tgt.Include)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid deployment.target.include %q: %v", tgt.Include, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if tgt.Exclude != "" {
|
|
|
|
tgt.excludeGlob, err = hglob.GetGlob(tgt.Exclude)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid deployment.target.exclude %q: %v", tgt.Exclude, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2018-12-20 14:22:03 -05:00
|
|
|
}
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
// Matcher represents configuration to be applied to files whose paths match
|
2018-12-20 14:22:03 -05:00
|
|
|
// a specified pattern.
|
2023-01-04 12:24:36 -05:00
|
|
|
type Matcher struct {
|
2018-12-20 14:22:03 -05:00
|
|
|
// Pattern is the string pattern to match against paths.
|
2019-05-03 19:38:05 -04:00
|
|
|
// Matching is done against paths converted to use / as the path separator.
|
2018-12-20 14:22:03 -05:00
|
|
|
Pattern string
|
|
|
|
|
|
|
|
// CacheControl specifies caching attributes to use when serving the blob.
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
2019-05-31 12:55:48 -04:00
|
|
|
CacheControl string
|
2018-12-20 14:22:03 -05:00
|
|
|
|
|
|
|
// ContentEncoding specifies the encoding used for the blob's content, if any.
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
|
2019-05-31 12:55:48 -04:00
|
|
|
ContentEncoding string
|
2018-12-20 14:22:03 -05:00
|
|
|
|
|
|
|
// ContentType specifies the MIME type of the blob being written.
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
|
2019-05-31 12:55:48 -04:00
|
|
|
ContentType string
|
2018-12-20 14:22:03 -05:00
|
|
|
|
|
|
|
// Gzip determines whether the file should be gzipped before upload.
|
|
|
|
// If so, the ContentEncoding field will automatically be set to "gzip".
|
|
|
|
Gzip bool
|
|
|
|
|
|
|
|
// Force indicates that matching files should be re-uploaded. Useful when
|
|
|
|
// other route-determined metadata (e.g., ContentType) has changed.
|
|
|
|
Force bool
|
|
|
|
|
|
|
|
// re is Pattern compiled.
|
|
|
|
re *regexp.Regexp
|
|
|
|
}
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
func (m *Matcher) Matches(path string) bool {
|
2018-12-20 14:22:03 -05:00
|
|
|
return m.re.MatchString(path)
|
|
|
|
}
|
|
|
|
|
2023-06-18 12:35:11 -04:00
|
|
|
var DefaultConfig = DeployConfig{
|
|
|
|
Workers: 10,
|
|
|
|
InvalidateCDN: true,
|
|
|
|
MaxDeletes: 256,
|
|
|
|
}
|
|
|
|
|
2023-01-04 12:24:36 -05:00
|
|
|
// DecodeConfig creates a config from a given Hugo configuration.
|
|
|
|
func DecodeConfig(cfg config.Provider) (DeployConfig, error) {
|
2023-06-18 12:35:11 -04:00
|
|
|
dcfg := DefaultConfig
|
2020-08-03 13:06:18 -04:00
|
|
|
|
2018-12-20 14:22:03 -05:00
|
|
|
if !cfg.IsSet(deploymentConfigKey) {
|
|
|
|
return dcfg, nil
|
|
|
|
}
|
|
|
|
if err := mapstructure.WeakDecode(cfg.GetStringMap(deploymentConfigKey), &dcfg); err != nil {
|
|
|
|
return dcfg, err
|
|
|
|
}
|
2023-01-04 12:24:36 -05:00
|
|
|
|
|
|
|
if dcfg.Workers <= 0 {
|
|
|
|
dcfg.Workers = 10
|
|
|
|
}
|
|
|
|
|
2020-02-27 01:26:05 -05:00
|
|
|
for _, tgt := range dcfg.Targets {
|
2023-01-04 12:24:36 -05:00
|
|
|
if *tgt == (Target{}) {
|
2021-11-30 16:39:47 -05:00
|
|
|
return dcfg, errors.New("empty deployment target")
|
|
|
|
}
|
2020-02-27 01:26:05 -05:00
|
|
|
if err := tgt.parseIncludeExclude(); err != nil {
|
|
|
|
return dcfg, err
|
|
|
|
}
|
|
|
|
}
|
2018-12-20 14:22:03 -05:00
|
|
|
var err error
|
|
|
|
for _, m := range dcfg.Matchers {
|
2023-01-04 12:24:36 -05:00
|
|
|
if *m == (Matcher{}) {
|
2021-11-30 16:39:47 -05:00
|
|
|
return dcfg, errors.New("empty deployment matcher")
|
|
|
|
}
|
2018-12-20 14:22:03 -05:00
|
|
|
m.re, err = regexp.Compile(m.Pattern)
|
|
|
|
if err != nil {
|
|
|
|
return dcfg, fmt.Errorf("invalid deployment.matchers.pattern: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2019-05-03 12:30:46 -04:00
|
|
|
for _, o := range dcfg.Order {
|
|
|
|
re, err := regexp.Compile(o)
|
|
|
|
if err != nil {
|
|
|
|
return dcfg, fmt.Errorf("invalid deployment.orderings.pattern: %v", err)
|
|
|
|
}
|
|
|
|
dcfg.ordering = append(dcfg.ordering, re)
|
|
|
|
}
|
2020-08-03 13:06:18 -04:00
|
|
|
|
2018-12-20 14:22:03 -05:00
|
|
|
return dcfg, nil
|
|
|
|
}
|