github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/source/local/local.go (about) 1 // Package local is a source backend for loading packages from a single directory. 2 package local 3 4 import ( 5 "context" 6 "os" 7 "path/filepath" 8 "strings" 9 "time" 10 11 "github.com/kubri/kubri/source" 12 ) 13 14 type Config struct { 15 Path string 16 Version string 17 } 18 19 type localSource struct { 20 path string 21 root string 22 version string 23 } 24 25 func New(c Config) (*source.Source, error) { 26 root, err := getRoot(c.Path) 27 if err != nil { 28 return nil, err 29 } 30 return source.New(&localSource{path: c.Path, root: root, version: c.Version}), nil 31 } 32 33 func (s *localSource) ListReleases(ctx context.Context) ([]*source.Release, error) { 34 r, err := s.GetRelease(ctx, s.version) 35 if err != nil { 36 return nil, err 37 } 38 39 return []*source.Release{r}, nil 40 } 41 42 func (s *localSource) GetRelease(ctx context.Context, version string) (*source.Release, error) { 43 files, err := getFiles(s.path) 44 if err != nil { 45 return nil, err 46 } 47 48 r := &source.Release{ 49 Version: version, 50 Date: time.Now(), 51 Assets: make([]*source.Asset, 0, len(files)), 52 } 53 54 for _, path := range files { 55 if err := ctx.Err(); err != nil { 56 return nil, err 57 } 58 59 f, err := os.Stat(path) 60 if err != nil { 61 return nil, err 62 } 63 64 path, err = filepath.Abs(path) 65 if err != nil { 66 return nil, err 67 } 68 69 r.Assets = append(r.Assets, &source.Asset{ 70 Name: f.Name(), 71 URL: "file://" + path, 72 Size: int(f.Size()), 73 }) 74 } 75 76 return r, nil 77 } 78 79 func (s *localSource) UploadAsset(_ context.Context, _, name string, data []byte) error { 80 return os.WriteFile(filepath.Join(s.root, name), data, os.ModePerm) 81 } 82 83 func (s *localSource) DownloadAsset(_ context.Context, _, name string) ([]byte, error) { 84 path := filepath.Join(s.root, name) 85 if _, err := os.Stat(path); err == nil { 86 return os.ReadFile(path) 87 } 88 89 files, err := getFiles(s.path) 90 if err != nil { 91 return nil, err 92 } 93 94 for _, path := range files { 95 if filepath.Base(path) == name { 96 return os.ReadFile(path) 97 } 98 } 99 100 return nil, source.ErrAssetNotFound 101 } 102 103 func getFiles(path string) ([]string, error) { 104 if strings.ContainsRune(path, '*') { 105 return filepath.Glob(path) 106 } 107 108 fi, err := os.Stat(path) 109 if err != nil { 110 return nil, err 111 } 112 113 if !fi.IsDir() { 114 return []string{path}, nil 115 } 116 117 files, err := os.ReadDir(path) 118 if err != nil { 119 return nil, err 120 } 121 122 paths := make([]string, 0, len(files)) 123 for _, file := range files { 124 if !file.IsDir() { 125 paths = append(paths, filepath.Join(path, file.Name())) 126 } 127 } 128 129 return paths, nil 130 } 131 132 func getRoot(path string) (string, error) { 133 if i := strings.IndexRune(path, '*'); i >= 0 { 134 return path[:i], nil 135 } 136 137 fi, err := os.Stat(path) 138 if err != nil { 139 return "", err 140 } 141 142 if !fi.IsDir() { 143 return filepath.Dir(path), nil 144 } 145 146 return path, nil 147 }