package models import ( "encoding/json" "errors" "fmt" "io/fs" "os" "path/filepath" "github.com/rs/zerolog/log" ) const ( MainDir string = ".homeserver" ComposeFile string = "docker-compose.deploy.yml" EnvFile string = ".env" NginxFile = "nginx.conf" ConfFile = "hmdeploy.json" ) var ErrProjectConfFile = errors.New("project error") func getFileInfo(baseDir, filePath string) (string, fs.FileInfo, error) { var fInf fs.FileInfo if !filepath.IsAbs(filePath) { filePath = filepath.Join(baseDir, filePath) filePath, err := filepath.Abs(filePath) if err != nil { return filePath, fInf, fmt.Errorf( "%w, file=%s, err=%v", ErrProjectConfFile, filePath, err, ) } } fInf, err := os.Stat(filePath) if err != nil { return filePath, fInf, fmt.Errorf( "%w, unable to stat file=%s, err=%v", ErrProjectConfFile, filePath, err, ) } return filePath, fInf, nil } // Project handles the details and file informations of your project. type Project struct { Name string `json:"name"` Dir string ImageNames []string `json:"images"` Deps struct { EnvFile string `json:"env"` EnvFileInfo fs.FileInfo ComposeFile string `json:"compose"` ComposeFileInfo fs.FileInfo NginxFile string `json:"nginx"` NginxFileInfo fs.FileInfo } `json:"dependencies"` } func (p *Project) validate() error { cpath, cfs, err := getFileInfo(p.Dir, p.Deps.ComposeFile) if err != nil { return err } p.Deps.ComposeFileInfo = cfs p.Deps.ComposeFile = cpath if p.Deps.EnvFile != "" { epath, efs, err := getFileInfo(p.Dir, p.Deps.EnvFile) if err != nil { return err } p.Deps.EnvFileInfo = efs p.Deps.EnvFile = epath } else { log.Warn().Msg("no .env file provided, hoping one it's set elsewhere...") } npath, nfs, err := getFileInfo(p.Dir, p.Deps.NginxFile) if err != nil { return err } p.Deps.NginxFileInfo = nfs p.Deps.NginxFile = npath return nil } // ProjectFromDir instantiates a new project from a directory path. // // The directory path must refers to the path including the `.homeserver` dir not // the `.homeserver` path itself. func ProjectFromDir(dir string) (Project, error) { var p Project dir = filepath.Join(dir, MainDir) p.Dir = dir content, err := os.ReadFile(filepath.Join(dir, ConfFile)) if err != nil { return p, fmt.Errorf( "%w, unable to read conf file=%s, err=%v", ErrProjectConfFile, ConfFile, err, ) } if err := json.Unmarshal(content, &p); err != nil { return p, fmt.Errorf( "%w, unable to parse conf file=%s, err=%v", ErrProjectConfFile, ConfFile, err, ) } if err := p.validate(); err != nil { return p, fmt.Errorf( "%w, unable to validate project, name=%s, dir=%s, err=%v", ErrProjectConfFile, p.Name, p.Dir, err, ) } return p, nil }