mirror of https://github.com/knative/func.git
214 lines
5.9 KiB
Go
214 lines
5.9 KiB
Go
package functions
|
|
|
|
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"
|
|
)
|
|
|
|
// 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.
|
|
// Blank indicates that the system was not instantiated to use any
|
|
// repositories on disk.
|
|
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 or insufficient permissions
|
|
if _, err = os.Stat(r.path); os.IsNotExist(err) || os.IsPermission(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)
|
|
}
|