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 = ".env" NginxFile = "nginx.conf" ConfFile = "hmdeploy.json" ) var ( ErrInvalidEnvFile = errors.New("unable to stat .env file") ErrInvalidComposeFile = errors.New("unable to stat compose file") ErrInvalidNginxFile = errors.New("unable to stat nginx file") ) func getFileInfo(baseDir, filePath string) (fs.FileInfo, error) { var fInf fs.FileInfo filePath = filepath.Clean(filePath) filePath = filepath.Join(baseDir, filePath) composePath, err := filepath.Abs(filePath) if err != nil { return fInf, fmt.Errorf("%w, %v", ErrInvalidComposeFile, err) } fInf, err = os.Stat(composePath) if err != nil { return fInf, fmt.Errorf("%w, %v", ErrInvalidComposeFile, err) } return fInf, nil } type Project struct { Name string `json:"name"` Dir string ImageName string `json:"image"` 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 { cfs, err := getFileInfo(p.Dir, p.Deps.ComposeFile) if err != nil { return fmt.Errorf("%w, %v", ErrInvalidComposeFile, err) } p.Deps.ComposeFileInfo = cfs if p.Deps.EnvFile != "" { efs, err := getFileInfo(p.Dir, p.Deps.EnvFile) if err != nil { return fmt.Errorf("%w, %v", ErrInvalidEnvFile, err) } p.Deps.EnvFileInfo = efs } else { log.Warn().Msg("no .env file provided, hoping one is set elsewhere...") } nfs, err := getFileInfo(p.Dir, p.Deps.EnvFile) if err != nil { return fmt.Errorf("%w, %v", ErrInvalidNginxFile, err) } p.Deps.NginxFileInfo = nfs return nil } 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 { log.Err(err).Str("dir", dir).Str("conf", ConfFile).Msg("unable to read conf file") return p, err } if err := json.Unmarshal(content, &p); err != nil { log.Err(err).Str("dir", dir).Str("conf", ConfFile).Msg("unable to parse conf file") return p, err } if err := p.validate(); err != nil { log.Err(err).Str("dir", dir).Msg("unable to validate project") return p, err } return p, nil }