deployConfig.go (4636B)
1 // Copyright 2019 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 //go:build !nodeploy 15 // +build !nodeploy 16 17 package deploy 18 19 import ( 20 "fmt" 21 "regexp" 22 23 "errors" 24 25 "github.com/gobwas/glob" 26 "github.com/gohugoio/hugo/config" 27 hglob "github.com/gohugoio/hugo/hugofs/glob" 28 "github.com/gohugoio/hugo/media" 29 "github.com/mitchellh/mapstructure" 30 ) 31 32 const deploymentConfigKey = "deployment" 33 34 // deployConfig is the complete configuration for deployment. 35 type deployConfig struct { 36 Targets []*target 37 Matchers []*matcher 38 Order []string 39 40 ordering []*regexp.Regexp // compiled Order 41 mediaTypes media.Types 42 } 43 44 type target struct { 45 Name string 46 URL string 47 48 CloudFrontDistributionID string 49 50 // GoogleCloudCDNOrigin specifies the Google Cloud project and CDN origin to 51 // invalidate when deploying this target. It is specified as <project>/<origin>. 52 GoogleCloudCDNOrigin string 53 54 // Optional patterns of files to include/exclude for this target. 55 // Parsed using github.com/gobwas/glob. 56 Include string 57 Exclude string 58 59 // Parsed versions of Include/Exclude. 60 includeGlob glob.Glob 61 excludeGlob glob.Glob 62 } 63 64 func (tgt *target) parseIncludeExclude() error { 65 var err error 66 if tgt.Include != "" { 67 tgt.includeGlob, err = hglob.GetGlob(tgt.Include) 68 if err != nil { 69 return fmt.Errorf("invalid deployment.target.include %q: %v", tgt.Include, err) 70 } 71 } 72 if tgt.Exclude != "" { 73 tgt.excludeGlob, err = hglob.GetGlob(tgt.Exclude) 74 if err != nil { 75 return fmt.Errorf("invalid deployment.target.exclude %q: %v", tgt.Exclude, err) 76 } 77 } 78 return nil 79 } 80 81 // matcher represents configuration to be applied to files whose paths match 82 // a specified pattern. 83 type matcher struct { 84 // Pattern is the string pattern to match against paths. 85 // Matching is done against paths converted to use / as the path separator. 86 Pattern string 87 88 // CacheControl specifies caching attributes to use when serving the blob. 89 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control 90 CacheControl string 91 92 // ContentEncoding specifies the encoding used for the blob's content, if any. 93 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding 94 ContentEncoding string 95 96 // ContentType specifies the MIME type of the blob being written. 97 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type 98 ContentType string 99 100 // Gzip determines whether the file should be gzipped before upload. 101 // If so, the ContentEncoding field will automatically be set to "gzip". 102 Gzip bool 103 104 // Force indicates that matching files should be re-uploaded. Useful when 105 // other route-determined metadata (e.g., ContentType) has changed. 106 Force bool 107 108 // re is Pattern compiled. 109 re *regexp.Regexp 110 } 111 112 func (m *matcher) Matches(path string) bool { 113 return m.re.MatchString(path) 114 } 115 116 // decode creates a config from a given Hugo configuration. 117 func decodeConfig(cfg config.Provider) (deployConfig, error) { 118 var ( 119 mediaTypesConfig []map[string]any 120 dcfg deployConfig 121 ) 122 123 if !cfg.IsSet(deploymentConfigKey) { 124 return dcfg, nil 125 } 126 if err := mapstructure.WeakDecode(cfg.GetStringMap(deploymentConfigKey), &dcfg); err != nil { 127 return dcfg, err 128 } 129 for _, tgt := range dcfg.Targets { 130 if *tgt == (target{}) { 131 return dcfg, errors.New("empty deployment target") 132 } 133 if err := tgt.parseIncludeExclude(); err != nil { 134 return dcfg, err 135 } 136 } 137 var err error 138 for _, m := range dcfg.Matchers { 139 if *m == (matcher{}) { 140 return dcfg, errors.New("empty deployment matcher") 141 } 142 m.re, err = regexp.Compile(m.Pattern) 143 if err != nil { 144 return dcfg, fmt.Errorf("invalid deployment.matchers.pattern: %v", err) 145 } 146 } 147 for _, o := range dcfg.Order { 148 re, err := regexp.Compile(o) 149 if err != nil { 150 return dcfg, fmt.Errorf("invalid deployment.orderings.pattern: %v", err) 151 } 152 dcfg.ordering = append(dcfg.ordering, re) 153 } 154 155 if cfg.IsSet("mediaTypes") { 156 mediaTypesConfig = append(mediaTypesConfig, cfg.GetStringMap("mediaTypes")) 157 } 158 159 dcfg.mediaTypes, err = media.DecodeTypes(mediaTypesConfig...) 160 if err != nil { 161 return dcfg, err 162 } 163 return dcfg, nil 164 }