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 }