summaryrefslogtreecommitdiff
path: root/internal/generator/generator.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-27 08:16:22 +0300
committerPaul Buetow <paul@buetow.org>2026-04-27 08:16:22 +0300
commit734c7fbd89241133499a88674d5cf62de2ca1469 (patch)
tree90aa48f5f892903a059b56e075e81821bcbda998 /internal/generator/generator.go
parent371c54cb5ad3793cf4b61e7451b0710d317021d6 (diff)
fix(generator): write to temp file, check close error, rename on success
Fixes data loss risk in writePage: - Write to a temp file instead of the target path. - Explicitly close the temp file and check the error (was ignored). - Rename to the final path only after successful close. - Remove the temp file on any error so truncated output is never left on disk. Added TestWritePage (happy path + template error) and TestWritePage_tempFileCleanedOnError to verify no corruption of existing file.
Diffstat (limited to 'internal/generator/generator.go')
-rw-r--r--internal/generator/generator.go25
1 files changed, 21 insertions, 4 deletions
diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index 079cc38..0b51edd 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -199,16 +199,33 @@ func writePage(tmpl *template.Template, posts []*post.Post, pageIndex, totalPage
filename := pageFilename(pageIndex)
path := filepath.Join(cfg.OutputDir, filename)
- f, err := os.Create(path)
+ tmpFile, err := os.CreateTemp(cfg.OutputDir, filename+".*.tmp")
if err != nil {
- return fmt.Errorf("create %s: %w", filename, err)
+ return fmt.Errorf("create temp for %s: %w", filename, err)
}
- defer f.Close()
+ tmpPath := tmpFile.Name()
- if err := tmpl.Execute(f, data); err != nil {
+ ok := false
+ defer func() {
+ _ = tmpFile.Close()
+ if !ok {
+ _ = os.Remove(tmpPath)
+ }
+ }()
+
+ if err := tmpl.Execute(tmpFile, data); err != nil {
return fmt.Errorf("render %s: %w", filename, err)
}
+ if err := tmpFile.Close(); err != nil {
+ return fmt.Errorf("close temp %s: %w", filename, err)
+ }
+
+ if err := os.Rename(tmpPath, path); err != nil {
+ return fmt.Errorf("rename %s: %w", filename, err)
+ }
+
+ ok = true
return nil
}