-
Notifications
You must be signed in to change notification settings - Fork 78
/
project.go
185 lines (149 loc) · 4.07 KB
/
project.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package project
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/fsutil"
"k8s.io/apimachinery/pkg/util/sets"
"kusionstack.io/kusion/pkg/apis/stack"
"kusionstack.io/kusion/pkg/log"
"kusionstack.io/kusion/pkg/util/yaml"
)
// IsProject determine whether the given path is Project directory
func IsProject(path string) bool {
f, err := os.Stat(path)
f2, err2 := os.Stat(filepath.Join(path, ProjectFile))
if (err == nil && f.IsDir()) && (err2 == nil && f2.Mode().IsRegular()) {
return true
}
return false
}
// IsProjectFile determine whether the given path is Project file
func IsProjectFile(path string) bool {
f, err := os.Stat(path)
return err == nil && !f.IsDir() && f.Mode().IsRegular() && filepath.Base(path) == ProjectFile
}
// FindProjectPath locates the closest project from the current working directory, or an error if not found.
func FindProjectPath() (string, error) {
dir, err := os.Getwd()
if err != nil {
return "", err
}
path, err := FindProjectPathFrom(dir)
if err != nil {
return "", err
}
return path, nil
}
// FindProjectPathFrom locates the closest project from the given path, searching "upwards" in the directory
// hierarchy. If no project is found, an empty path is returned.
func FindProjectPathFrom(path string) (string, error) {
file, err := fsutil.WalkUp(path, IsProjectFile, func(s string) bool {
return true
})
if err != nil {
return "", err
}
return filepath.Dir(file), nil
}
// ParseConfiguration parse the project configuration by the given directory
func ParseConfiguration(path string) (*Configuration, error) {
if !IsProject(path) {
return nil, ErrNotProjectDirectory
}
var config Configuration
err := yaml.ParseYamlFromFile(filepath.Join(path, ProjectFile), &config)
if err != nil {
return nil, err
}
return &config, nil
}
// FindAllProjects find all projects from the current working directory
func FindAllProjects() ([]*Project, error) {
dir, err := os.Getwd()
if err != nil {
return nil, err
}
projects, err := FindAllProjectsFrom(dir)
if err != nil {
return nil, err
}
return projects, nil
}
// FindAllProjectsFrom find all project from the given path
func FindAllProjectsFrom(path string) ([]*Project, error) {
projects := []*Project{}
s := sets.NewString()
err := filepath.WalkDir(path, func(p string, _ fs.DirEntry, _ error) error {
if IsProject(p) && !s.Has(p) {
// Parse project configuration
config, err := ParseConfiguration(p)
if err != nil {
log.Error(err)
return fmt.Errorf("parse project.yaml failed. %w", err)
}
// Find all stacks
stacks, err := stack.FindAllStacksFrom(p)
if err != nil {
log.Error(err)
return fmt.Errorf("parse stacks failed. %w", err)
}
// Get absolute path
absPath, err := filepath.Abs(p)
if err != nil {
log.Error(err)
return fmt.Errorf("project path failed. %w", err)
}
projects = append(projects, NewProject(config, absPath, stacks))
}
return nil
})
return projects, err
}
// GetProject get project from the current working directory
func GetProject() (*Project, error) {
dir, err := os.Getwd()
if err != nil {
return nil, err
}
project, err := GetProjectFrom(dir)
if err != nil {
return nil, err
}
return project, nil
}
// GetProjectFrom get project from the given path
func GetProjectFrom(path string) (*Project, error) {
if !IsProject(path) {
return nil, ErrNotProjectDirectory
}
projects, err := FindAllProjectsFrom(path)
if err != nil {
return nil, err
}
if len(projects) != 1 {
return nil, ErrProjectNotUnique
}
return projects[0], nil
}
// DetectProjectAndStack try to get stack and project from given path
func DetectProjectAndStack(stackDir string) (p *Project, s *stack.Stack, err error) {
stackDir, err = filepath.Abs(stackDir)
if err != nil {
return nil, nil, err
}
s, err = stack.GetStackFrom(stackDir)
if err != nil {
return nil, nil, err
}
projectDir, err := FindProjectPathFrom(stackDir)
if err != nil {
return nil, nil, err
}
p, err = GetProjectFrom(projectDir)
if err != nil {
return nil, nil, err
}
return p, s, nil
}