| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package dubbo |
| |
| import ( |
| "errors" |
| "fmt" |
| "os" |
| "path/filepath" |
| "strings" |
| ) |
| |
| const ( |
| 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 lea of embedded and extensible. |
| // Enables single-repository mode. This replaces the default embedded repo |
| // and extended repositories. This is an important mode for both disk less |
| // (config-less) operation, such as security-restricted 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 repositories 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 |
| } |
| |
| var 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) |
| } |