package sitebuilder import ( "context" "errors" "lmika.dev/lmika/hugo-cms/models" "lmika.dev/lmika/hugo-cms/providers/bus" "lmika.dev/lmika/hugo-cms/providers/db" "lmika.dev/lmika/hugo-cms/providers/git" "lmika.dev/lmika/hugo-cms/providers/hugo" "lmika.dev/lmika/hugo-cms/providers/netlify" "lmika.dev/lmika/hugo-cms/providers/themes" "log" "os" "path/filepath" ) type Service struct { db *db.DB themes *themes.Provider git *git.Provider hugo *hugo.Provider netlify *netlify.Provider bus *bus.Bus } func New( db *db.DB, themes *themes.Provider, git *git.Provider, hugo *hugo.Provider, netlify *netlify.Provider, bus *bus.Bus, ) *Service { return &Service{ db: db, themes: themes, git: git, hugo: hugo, netlify: netlify, bus: bus, } } func (s *Service) CreateNewSite(site models.Site) models.Job { return models.Job{ Do: func(ctx context.Context) error { s.signalSiteBuildingStarted(ctx, site) defer s.signalSiteBuildingFinished(ctx, site) return s.createSite(ctx, site) }, } } func (s *Service) RebuildSite(oldSite, newSite models.Site) models.Job { return models.Job{ Do: func(ctx context.Context) error { s.signalSiteBuildingStarted(ctx, newSite) defer s.signalSiteBuildingFinished(ctx, newSite) return s.rebuildSite(ctx, oldSite, newSite) }, } } func (s *Service) RebuildSiteContent(oldSite, newSite models.Site) models.Job { return models.Job{ Do: func(ctx context.Context) error { s.signalSiteBuildingStarted(ctx, newSite) defer s.signalSiteBuildingFinished(ctx, newSite) return s.rebuildContent(ctx, oldSite, newSite) }, } } func (s *Service) rebuildSite(ctx context.Context, oldSite, newSite models.Site) error { // Teardown the existing site siteDir := s.hugo.SiteStagingDir(oldSite, hugo.BaseSiteDir) if err := os.RemoveAll(siteDir); err != nil { return err } if err := s.createSite(ctx, newSite); err != nil { return err } if err := s.writeAllContent(ctx, newSite); err != nil { return err } return s.publish(ctx, newSite) } func (s *Service) rebuildContent(ctx context.Context, oldSite, newSite models.Site) error { // Teardown the existing site siteDir := s.hugo.SiteStagingDir(oldSite, hugo.ContentSiteDir) if err := os.RemoveAll(siteDir); err != nil { return err } if err := s.writeAllContent(ctx, newSite); err != nil { return err } return s.publish(ctx, newSite) } func (s *Service) writeAllContent(ctx context.Context, newSite models.Site) error { if err := s.writeAllPosts(ctx, newSite); err != nil { return err } if err := s.writeAllPages(ctx, newSite); err != nil { return err } return nil } func (s *Service) fullRebuildNecessary(ctx context.Context, site models.Site) (bool, error) { dirsMustExists := []string{ s.hugo.SiteStagingDir(site, hugo.BaseSiteDir), s.hugo.SiteStagingDir(site, hugo.ThemeSiteDir), s.hugo.SiteStagingDir(site, hugo.ContentSiteDir), } filesMustExists := []string{ filepath.Join(s.hugo.SiteStagingDir(site, hugo.BaseSiteDir), "hugo.yaml"), } for _, dir := range dirsMustExists { if stat, err := os.Stat(dir); err != nil { if errors.Is(err, os.ErrNotExist) { return true, nil } else { return false, err } } else if !stat.IsDir() { return true, nil } } for _, file := range filesMustExists { if _, err := os.Stat(file); err != nil { if errors.Is(err, os.ErrNotExist) { return true, nil } else { return false, err } } } return false, nil } func (s *Service) createSite(ctx context.Context, site models.Site) error { themeMeta, ok := s.themes.Lookup(site.Theme) if !ok { return errors.New("theme not found") } // Build the site log.Printf(" .. build") if err := s.hugo.NewSite(ctx, site); err != nil { return err } // Setup the theme log.Printf(" .. theme") if err := s.git.Clone(ctx, themeMeta.URL, s.hugo.SiteStagingDir(site, hugo.ThemeSiteDir)); err != nil { return err } if err := s.hugo.ReconfigureSite(ctx, false, "hugo", site); err != nil { return err } if err := s.hugo.ReconfigureSite(ctx, true, "hugoPreview", site); err != nil { return err } return nil } func (s *Service) signalSiteBuildingStarted(ctx context.Context, site models.Site) { s.bus.Fire(models.Event{Type: models.EventSiteBuildingStart, Data: site}) } func (s *Service) signalSiteBuildingFinished(ctx context.Context, site models.Site) { s.bus.Fire(models.Event{Type: models.EventSiteBuildingDone, Data: site}) }