From 0d016129a425d7014c5b8e2ec8eea50962f9ede3 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Sat, 7 May 2022 14:33:57 -0600 Subject: Implement import-posts script --- srv/src/cmd/import-posts/main.go | 169 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 srv/src/cmd/import-posts/main.go (limited to 'srv/src/cmd/import-posts') diff --git a/srv/src/cmd/import-posts/main.go b/srv/src/cmd/import-posts/main.go new file mode 100644 index 0000000..6a1e952 --- /dev/null +++ b/srv/src/cmd/import-posts/main.go @@ -0,0 +1,169 @@ +package main + +import ( + "context" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" + "time" + + "github.com/adrg/frontmatter" + "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg" + cfgpkg "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg" + "github.com/mediocregopher/blog.mediocregopher.com/srv/post" + "github.com/mediocregopher/mediocre-go-lib/v2/mctx" + "github.com/mediocregopher/mediocre-go-lib/v2/mlog" +) + +type postFrontmatter struct { + Title string `yaml:"title"` + Description string `yaml:"description"` + Tags string `yaml:"tags"` + Series string `yaml:"series"` + Updated string `yaml:"updated"` +} + +func parseDate(str string) (time.Time, error) { + const layout = "2006-01-02" + return time.Parse(layout, str) +} + +var postNameRegexp = regexp.MustCompile(`(20..-..-..)-([^.]+).md`) + +func importPost(postStore post.Store, path string) (post.StoredPost, error) { + + fileName := filepath.Base(path) + fileNameMatches := postNameRegexp.FindStringSubmatch(fileName) + + if len(fileNameMatches) != 3 { + return post.StoredPost{}, fmt.Errorf("file name %q didn't match regex", fileName) + } + + publishedAtStr := fileNameMatches[1] + publishedAt, err := parseDate(publishedAtStr) + if err != nil { + return post.StoredPost{}, fmt.Errorf("parsing publish date %q: %w", publishedAtStr, err) + } + + postID := fileNameMatches[2] + + f, err := os.Open(path) + if err != nil { + return post.StoredPost{}, fmt.Errorf("opening file: %w", err) + } + defer f.Close() + + var matter postFrontmatter + + body, err := frontmatter.Parse(f, &matter) + + if err != nil { + return post.StoredPost{}, fmt.Errorf("parsing frontmatter: %w", err) + } + + // if there is already a post for this ID, delete it, we're overwriting + if err := postStore.Delete(postID); err != nil { + return post.StoredPost{}, fmt.Errorf("deleting post id %q: %w", postID, err) + } + + p := post.Post{ + ID: postID, + Title: matter.Title, + Description: matter.Description, + Tags: strings.Fields(matter.Tags), + Series: matter.Series, + Body: string(body), + } + + if err := postStore.Set(p, publishedAt); err != nil { + return post.StoredPost{}, fmt.Errorf("storing post id %q: %w", p.ID, err) + } + + if matter.Updated != "" { + + lastUpdatedAt, err := parseDate(matter.Updated) + if err != nil { + return post.StoredPost{}, fmt.Errorf("parsing updated date %q: %w", matter.Updated, err) + } + + // as a hack, we store the post again with the updated date as now. This + // will update the LastUpdatedAt field in the Store. + if err := postStore.Set(p, lastUpdatedAt); err != nil { + return post.StoredPost{}, fmt.Errorf("updating post id %q: %w", p.ID, err) + } + } + + storedPost, err := postStore.GetByID(p.ID) + if err != nil { + return post.StoredPost{}, fmt.Errorf("retrieving stored post by id %q: %w", p.ID, err) + } + + return storedPost, nil +} + +func main() { + + ctx := context.Background() + + cfg := cfg.NewBlogCfg(cfg.Params{}) + + var dataDir cfgpkg.DataDir + dataDir.SetupCfg(cfg) + defer dataDir.Close() + ctx = mctx.WithAnnotator(ctx, &dataDir) + + paths := cfg.Args("") + + // initialization + err := cfg.Init(ctx) + + logger := mlog.NewLogger(nil) + defer logger.Close() + + logger.Info(ctx, "process started") + defer logger.Info(ctx, "process exiting") + + if err != nil { + logger.Fatal(ctx, "initializing", err) + } + + if len(*paths) == 0 { + logger.FatalString(ctx, "no paths given") + } + + postStore, err := post.NewStore(post.StoreParams{ + DataDir: dataDir, + }) + if err != nil { + logger.Fatal(ctx, "initializing post store", err) + } + defer postStore.Close() + + for _, path := range *paths { + + ctx := mctx.Annotate(ctx, "postPath", path) + + storedPost, err := importPost(postStore, path) + if err != nil { + logger.Error(ctx, "importing post", err) + } + + ctx = mctx.Annotate(ctx, + "postID", storedPost.ID, + "postTitle", storedPost.Title, + "postDescription", storedPost.Description, + "postTags", storedPost.Tags, + "postSeries", storedPost.Series, + "postPublishedAt", storedPost.PublishedAt, + ) + + if !storedPost.LastUpdatedAt.IsZero() { + ctx = mctx.Annotate(ctx, + "postLastUpdatedAt", storedPost.LastUpdatedAt) + } + + logger.Info(ctx, "post stored") + } +} -- cgit v1.2.3