-
Notifications
You must be signed in to change notification settings - Fork 0
/
discovery.go
123 lines (101 loc) · 3.15 KB
/
discovery.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package entrypoint
import (
"fmt"
"io/fs"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/gosimple/slug"
)
// EntrypointFactory represents a factory for creating Entrypoints
type EntrypointFactory interface {
MakeEntrypoint(basedir, realpath string, isFile bool) (*Entrypoint, error)
}
// EntrypointDiscoverySpec represents a specification for discovering Entrypoint directories in a repository
type EntrypointDiscoverySpec struct {
Type EntrypointType `json:"type"`
Regex regexp.Regexp `json:"regex"`
Files bool `json:"files"`
Context map[string]interface{} `json:"context"`
}
// MakeEntrypoint attepmpts to create an Entrypoint from a given path
func (epds EntrypointDiscoverySpec) MakeEntrypoint(basedir, repoPath string, isFile bool) (*Entrypoint, error) {
if !epds.Files && isFile {
return nil, nil
}
if matches, ok := regexNamedMatches(repoPath, epds.Regex); ok {
epctx := make(map[string]interface{})
for k, v := range epds.Context {
epctx[k] = v
}
for k, v := range matches {
epctx[k] = v
}
name := ""
if n, ok := epctx["name"]; ok {
if ns, ok := n.(string); ok {
name = ns
}
}
if name == "" {
name = repoPath
}
name = slug.Make(name)
epType := epds.Type
if ctxType, ok := epctx["type"]; ok && epType == "" {
if ctxTypeStr, ok := ctxType.(string); ok {
epType = EntrypointType(ctxTypeStr)
}
}
epd := path.Join(basedir, repoPath)
if !isValidEntrypoint(epd, epType) {
fmt.Printf("%s is not a valid %q entrypoint\n", repoPath, epType)
return nil, nil
}
fmt.Printf("got entrypoint %q with regex %q\n", name, epds.Regex.String())
ep := Entrypoint{
Name: name,
Directory: repoPath,
Type: epType,
Context: epctx,
}
return &ep, nil
}
return nil, nil
}
var _ EntrypointFactory = EntrypointDiscoverySpec{}
// TODO: Make this more performant, add a flag to only check dir names, include a basedir prop to limit search context
// DiscoverEntrypoints walks a directory and returns a list of Entrypoints matching the supplied specs
func DiscoverEntrypoints(directory string, specs []EntrypointFactory) ([]Entrypoint, error) {
directory = path.Clean(directory)
entrypoints := []Entrypoint{}
err := filepath.WalkDir(directory, func(path string, d fs.DirEntry, err error) error {
realpath := strings.TrimLeft(path[len(directory):], "/")
if len(realpath) >= 4 && realpath[0:4] == ".git" {
return nil
}
for _, s := range specs {
ep, err := s.MakeEntrypoint(directory, realpath, !d.IsDir())
if err != nil {
return err
}
if ep != nil {
entrypoints = append(entrypoints, *ep)
}
}
return nil
})
if err != nil {
return nil, err
}
return entrypoints, nil
}
type cfnMinimalResource struct {
Properties map[string]interface{} `json:"Properties" yaml:"Properties"`
Metadata map[string]interface{} `json:"Metadata" yaml:"Metadata"`
}
// cfnMinimalTemplate is a minimal CloudFormation template that can be used to validate a file is actually a CloudFormation template
type cfnMinimalTemplate struct {
Resources map[string]cfnMinimalResource `json:"AWSTemplateFormatVersion" yaml:"AWSTemplateFormatVersion"`
}