forked from knative/func
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrepositories.go
219 lines (194 loc) · 6.06 KB
/
repositories.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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package function
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
)
const (
// DefaultRepositoryName is the name by which the currently default repo can
// be referred. This name is assumed when no template prefix is provided
// when determining a template canonical (full) name.
// Unless a single-repo override is defined, this is usually referring to the
// builtin (embedded) repository.
DefaultRepositoryName = "default"
// DefaultRepositoriesPath is the default location for repositories under
// management on local disk.
// TODO: the logic which defaults this to ~/.config/func/repositories will
// be moved from the CLI to the core in the near future. For now use the
// current working directory.
DefaultRepositoriesPath = ""
)
// Repositories manager
type Repositories struct {
// Optional path to extensible repositories on disk. Blank indicates not
// to use extensible
path string
// Optional uri of a single repo to use in leau of embedded and extensible.
// Enables single-repository mode. This replaces the default embedded repo
// and extended repositories. This is an important mode for both diskless
// (config-less) operation, such as security-restrited environments, and for
// running as a library in which case environmental settings should be
// ignored in favor of a more functional approach in which only inputs affect
// outputs.
remote string
// backreference to the client enabling this repositorires manager to
// have full API access.
client *Client
}
// newRepositories manager
// contains a backreference to the client (type tree root) for access to the
// full client API during implementations.
func newRepositories(client *Client) *Repositories {
return &Repositories{
client: client,
path: client.repositoriesPath,
remote: client.repositoriesURI,
}
}
// Path returns the currently active repositories path under management.
func (r *Repositories) Path() string {
return r.path
}
// List all repositories the current configuration of the repo manager has
// defined.
func (r *Repositories) List() ([]string, error) {
repositories, err := r.All()
if err != nil {
return []string{}, err
}
names := []string{}
for _, repo := range repositories {
names = append(names, repo.Name)
}
return names, nil
}
// All repositories under management
// The default repository is always first.
// If a path to custom repositories is defined, these are included next.
// If repositories is in single-repo mode, it will be the only repo returned.
func (r *Repositories) All() (repos []Repository, err error) {
var repo Repository
// if in single-repo mode:
// Create a new repository from the remote URI, and set its name to
// the default so that it is treated as the default in place of the embedded.
if r.remote != "" {
if repo, err = NewRepository(DefaultRepositoryName, r.remote); err != nil {
return
}
repos = []Repository{repo}
return
}
// When not in single-repo mode (above), the default repository is always
// first in the list
if repo, err = NewRepository("", ""); err != nil {
return
}
repos = append(repos, repo)
// Do not continue on to loading extended repositories unless path defined
// and it exists.
if r.path == "" {
return
}
// Return empty if path does not exist
if _, err = os.Stat(r.path); os.IsNotExist(err) {
return repos, nil
}
// Load each repo from disk.
// All settings, including name, are derived from its structure on disk
// plus manifest.
ff, err := os.ReadDir(r.path)
if err != nil {
return
}
for _, f := range ff {
if !f.IsDir() || strings.HasPrefix(f.Name(), ".") {
continue
}
var abspath string
abspath, err = filepath.Abs(r.path)
if err != nil {
return
}
if repo, err = NewRepository("", "file://"+filepath.ToSlash(abspath)+"/"+f.Name()); err != nil {
return
}
repos = append(repos, repo)
}
return
}
// Get a repository by name, error if it does not exist.
func (r *Repositories) Get(name string) (repo Repository, err error) {
all, err := r.All()
if err != nil {
return
}
if len(all) == 0 { // should not be possible because embedded always exists.
err = errors.New("internal error: no repositories loaded")
return
}
if name == DefaultRepositoryName {
repo = all[0]
return
}
if r.remote != "" {
return repo, fmt.Errorf("in single-repo mode (%v). Repository '%v' not loaded", r.remote, name)
}
for _, v := range all {
if v.Name == name {
repo = v
return
}
}
return repo, ErrRepositoryNotFound
}
// Add a repository of the given name from the URI. Name, if not provided,
// defaults to the repo name (sans optional .git suffix). Returns the final
// name as added.
func (r *Repositories) Add(name, uri string) (string, error) {
if r.path == "" {
return "", fmt.Errorf("repository %v(%v) not added. "+
"No repositories path provided", name, uri)
}
// Create a repo (in-memory FS) from the URI
repo, err := NewRepository(name, uri)
if err != nil {
return "", fmt.Errorf("failed to create new repository: %w", err)
}
// Error if the repository already exists on disk
dest := filepath.Join(r.path, repo.Name)
if _, err := os.Stat(dest); !os.IsNotExist(err) {
return "", fmt.Errorf("repository '%v' already exists", repo.Name)
}
// Instruct the repository to write itself to disk at the given path.
// Fails if path exists.
err = repo.Write(dest)
if err != nil {
return "", fmt.Errorf("failed to write repository: %w", err)
}
return repo.Name, nil
}
// Rename a repository
func (r *Repositories) Rename(from, to string) error {
if r.path == "" {
return fmt.Errorf("repository %v not renamed. "+
"No repositories path provided", from)
}
a := filepath.Join(r.path, from)
b := filepath.Join(r.path, to)
return os.Rename(a, b)
}
// Remove a repository of the given name from the repositories.
// (removes its directory in Path)
func (r *Repositories) Remove(name string) error {
if r.path == "" {
return fmt.Errorf("repository %v not removed. "+
"No repositories path provided", name)
}
if name == "" {
return errors.New("name is required")
}
path := filepath.Join(r.path, name)
return os.RemoveAll(path)
}