package models import ( "encoding/json" "errors" "fmt" "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 getFilepath(baseDir, filePath string) (string, error) { filePath = filepath.Join(baseDir, filePath) if !filepath.IsAbs(filePath) { filePath, err := filepath.Abs(filePath) //nolint: govet if err != nil { return filePath, fmt.Errorf( "%w, file=%s, err=%v", ErrProjectConfFile, filePath, err, ) } } fileInfo, err := os.Stat(filePath) if err != nil { return filePath, fmt.Errorf( "%w, file=%s, err=%v", ErrProjectConfFile, filePath, err, ) } if fileInfo.IsDir() { return filePath, fmt.Errorf( "%w, file=%s, err=%s", ErrProjectConfFile, filePath, "must be a file", ) } return filePath, nil } // Project handles the details and file informations of your project. type Project struct { Name string `json:"name"` Dir string Deps struct { EnvFile string `json:"env"` ComposeFile string `json:"compose"` NginxFile string `json:"nginx"` } `json:"dependencies"` ImageNames []string `json:"images"` } func (p *Project) validate() error { cpath, err := getFilepath(p.Dir, p.Deps.ComposeFile) if err != nil { return err } p.Deps.ComposeFile = cpath if p.Deps.EnvFile != "" { epath, err := getFilepath(p.Dir, p.Deps.EnvFile) if err != nil { return err } p.Deps.EnvFile = epath } else { log.Warn().Msg("no .env file provided, hoping one it's set elsewhere...") } if p.Deps.NginxFile != "" { npath, err := getFilepath(p.Dir, p.Deps.NginxFile) if err != nil { return err } p.Deps.NginxFile = npath } else { log.Warn().Msg("no Nginx conf file provided, Nginx deployment discarded") } 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 }