7
7
"maps"
8
8
"net/http"
9
9
"os"
10
+ "path/filepath"
10
11
11
12
"github.com/pkg/errors"
12
13
"github.com/samber/lo"
@@ -71,7 +72,12 @@ func readFromFile(path string) (*Config, error) {
71
72
if err != nil {
72
73
return nil , err
73
74
}
74
- return loadBytes (b )
75
+ config , err := loadBytes (b )
76
+ if err != nil {
77
+ return nil , err
78
+ }
79
+ config .Root .AbsRootPath = path
80
+ return config , nil
75
81
}
76
82
77
83
func LoadConfigFromURL (ctx context.Context , url string ) (* Config , error ) {
@@ -104,19 +110,40 @@ func loadBytes(b []byte) (*Config, error) {
104
110
}
105
111
106
112
func (c * Config ) LoadRecursive (lockfile * lock.File ) error {
113
+ return c .loadRecursive (lockfile , map [string ]bool {}, "" /*cyclePath*/ )
114
+ }
115
+
116
+ // loadRecursive loads all the included plugins and their included plugins, etc.
117
+ // seen should be a cloned map because loading plugins twice is allowed if they
118
+ // are in different paths.
119
+ func (c * Config ) loadRecursive (
120
+ lockfile * lock.File ,
121
+ seen map [string ]bool ,
122
+ cyclePath string ,
123
+ ) error {
107
124
included := make ([]* Config , 0 , len (c .Root .Include ))
108
125
109
126
for _ , includeRef := range c .Root .Include {
110
- pluginConfig , err := plugin .LoadConfigFromInclude (includeRef , lockfile )
127
+ pluginConfig , err := plugin .LoadConfigFromInclude (
128
+ includeRef , lockfile , filepath .Dir (c .Root .AbsRootPath ))
111
129
if err != nil {
112
130
return errors .WithStack (err )
113
131
}
114
132
115
- includable := & Config {
116
- Root : pluginConfig .ConfigFile ,
117
- pluginData : & pluginConfig .PluginOnlyData ,
133
+ newCyclePath := fmt .Sprintf ("%s -> %s" , cyclePath , includeRef )
134
+ if seen [pluginConfig .Source .Hash ()] {
135
+ // Note that duplicate includes are allowed if they are in different paths
136
+ // e.g. 2 different plugins can include the same plugin.
137
+ // We do not allow a single plugin to include duplicates.
138
+ return errors .Errorf (
139
+ "circular or duplicate include detected:\n %s" , newCyclePath )
118
140
}
119
- if err := includable .LoadRecursive (lockfile ); err != nil {
141
+ seen [pluginConfig .Source .Hash ()] = true
142
+
143
+ includable := createIncludableFromPluginConfig (pluginConfig )
144
+
145
+ if err := includable .loadRecursive (
146
+ lockfile , maps .Clone (seen ), newCyclePath ); err != nil {
120
147
return errors .WithStack (err )
121
148
}
122
149
@@ -136,7 +163,9 @@ func (c *Config) LoadRecursive(lockfile *lock.File) error {
136
163
Root : builtIn .ConfigFile ,
137
164
pluginData : & builtIn .PluginOnlyData ,
138
165
}
139
- if err := includable .LoadRecursive (lockfile ); err != nil {
166
+ newCyclePath := fmt .Sprintf ("%s -> %s" , cyclePath , builtIn .Source .LockfileKey ())
167
+ if err := includable .loadRecursive (
168
+ lockfile , maps .Clone (seen ), newCyclePath ); err != nil {
140
169
return errors .WithStack (err )
141
170
}
142
171
included = append (included , includable )
@@ -259,3 +288,14 @@ func (c *Config) IsEnvsecEnabled() bool {
259
288
}
260
289
return c .Root .IsEnvsecEnabled ()
261
290
}
291
+
292
+ func createIncludableFromPluginConfig (pluginConfig * plugin.Config ) * Config {
293
+ includable := & Config {
294
+ Root : pluginConfig .ConfigFile ,
295
+ pluginData : & pluginConfig .PluginOnlyData ,
296
+ }
297
+ if localPlugin , ok := pluginConfig .Source .(* plugin.LocalPlugin ); ok {
298
+ includable .Root .AbsRootPath = localPlugin .Path ()
299
+ }
300
+ return includable
301
+ }
0 commit comments