package external import ( "fmt" "os" "path/filepath" ) // LoadDir сканирует директорию modules.d/ — по одной поддиректории на // модуль, каждая с manifest.json и бинарниками под все заявленные // архитектуры. Отсутствие бинарника под конкретную архитектуру — не // фатально на этапе загрузки (модуль может использоваться только для // хостов другой архитектуры), но резолвится в ошибку в момент деплоя. func LoadDir(dir string) ([]*Module, error) { entries, err := os.ReadDir(dir) if os.IsNotExist(err) { return nil, nil // modules.d/ отсутствует — это нормально, внешних модулей просто нет } if err != nil { return nil, fmt.Errorf("читаю %s: %w", dir, err) } var modules []*Module for _, e := range entries { if !e.IsDir() { continue } moduleDir := filepath.Join(dir, e.Name()) manifestPath := filepath.Join(moduleDir, "manifest.json") if _, err := os.Stat(manifestPath); err != nil { continue // директория без manifest.json — не модуль, пропускаем молча } m, err := LoadManifest(manifestPath) if err != nil { return nil, fmt.Errorf("модуль в %s: %w", moduleDir, err) } binPaths := map[string]string{} for _, arch := range m.Architectures { p := binaryPath(moduleDir, m.Name, arch) if _, err := os.Stat(p); err == nil { binPaths[arch] = p } } modules = append(modules, &Module{ Manifest: *m, BinPath: binPaths, }) } return modules, nil }