~adnano/kiln-devel

tasks: add tasks.feeds support v2 APPLIED

oliverpool: 1
 tasks: add tasks.feeds support

 6 files changed, 126 insertions(+), 18 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~adnano/kiln-devel/patches/24903/mbox | git am -3
Learn more about email & git

[PATCH v2] tasks: add tasks.feeds support Export this patch

Every task can now have a [[tasks.feeds]] entry to generate one or more feeds.
It must have the following arguments:

[[tasks.feeds]]
input_dir = ""
title = "Example feed"
template = "atom.xml"
output = "atom.xml"
---
I think having [[tasks.feeds]] is fine. Yes it can generate arbitrary files,
but since they will be provided with the .Pages variable, they will be some
kind of feed or index.

If you have another naming suggestion, I can adjust this patch.
I have added a Dir.path field, to cleanup the Trim stuff. What do you think
about this?
I can revert to Permalink (but I can't find a suitable name for the input_dir
configuration - so I would appreciate your help here).
I have added some comments regarding the position of the feeds processing.

I have also added some logging regarding the deprecated [feeds] configuration,
as well as some hints to ease the transition.

The docs has been updated as well.
 config.toml                 |  9 +++--
 dir.go                      | 71 +++++++++++++++++++++++++++++++++----
 docs/kiln.1.scd             | 25 +++++++++----
 main.go                     |  2 +-
 site.go                     | 35 ++++++++++++++++++
 templates/_default/atom.xml |  2 +-
 6 files changed, 126 insertions(+), 18 deletions(-)

diff --git a/config.toml b/config.toml
index 7fd55af..9aeb85d 100644
--- a/config.toml
+++ b/config.toml
@@ -1,9 +1,6 @@
title = "Example website"
urls = ["gemini://example.com"]

[feeds]
"/" = "Example feed"

[permalinks]
"/" = "/{{ .Date.Format `2006/01/02` }}/{{ path.Base .Permalink }}/"

@@ -13,3 +10,9 @@ output = ".gmi"
template = ".gmi"
static_dir = "static"
output_dir = "public"

[[tasks.feeds]]
input_dir = ""
title = "Example feed"
template = "atom.xml"
output = "atom.xml"
diff --git a/dir.go b/dir.go
index e0a2505..24f63bb 100644
--- a/dir.go
+++ b/dir.go
@@ -18,11 +18,13 @@ import (

// Dir represents a directory.
type Dir struct {
	Permalink string
	Pages     []*Page
	Dirs      []*Dir
	index     *Page  // The index page.
	feed      []byte // Atom feed.
	Permalink  string
	Pages      []*Page
	Dirs       []*Dir
	index      *Page             // The index page.
	feed       []byte            // Atom feed (deprecated)
	extraFiles map[string][]byte // Atom/RSS feeds
	path       string            // relative to the content dir
}

// Page represents a page.
@@ -57,7 +59,7 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {
				continue
			}
			// Gather directory data
			dir := &Dir{Permalink: "/" + path + "/"}
			dir := &Dir{Permalink: "/" + path + "/", path: path}
			if err := dir._read(srcDir, path, task, cfg); err != nil {
				return err
			}
@@ -151,6 +153,21 @@ func (d *Dir) _read(srcDir, path string, task *Task, cfg *Site) error {

// process processes the directory's contents.
func (d *Dir) process(cfg *Site, task *Task) error {
	// build feeds
	// this must happen before the page processing
	// to have an unprocessed Page.Content
	// 192: d.Pages[i].Content = b.String()
	for _, feed := range task.Feeds {
		if d.path != feed.InputDir {
			continue
		}
		b, err := d.buildFeed(cfg, feed)
		if err != nil {
			return err
		}
		d.addExtraFile(feed.Output, b)
	}

	if task.TemplateExt != "" {
		// Create index
		if d.index != nil {
@@ -214,6 +231,39 @@ func (d *Dir) process(cfg *Site, task *Task) error {
	return nil
}

// buildFeed build the feed of the directory
func (d Dir) buildFeed(cfg *Site, feed Feed) ([]byte, error) {
	tmpl, ok := cfg.templates.FindTemplate(d.Permalink, feed.Template)
	if !ok {
		return nil, fmt.Errorf("missing feed template %q to generate %q", feed.Template, feed.Title)
	}

	var b bytes.Buffer
	data := struct {
		Title     string    // Feed title.
		Permalink string    // Feed permalink.
		Updated   time.Time // Last updated time.
		Pages     []*Page   // Feed pages.
	}{
		Title:     feed.Title,
		Permalink: d.Permalink,
		Updated:   time.Now(),
		Pages:     d.Pages,
	}
	if err := tmpl.Execute(&b, data); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}

func (d *Dir) addExtraFile(name string, content []byte) {
	if d.extraFiles == nil {
		d.extraFiles = map[string][]byte{name: content}
	} else {
		d.extraFiles[name] = content
	}
}

// write writes the directory's contents to the provided destination path.
func (d *Dir) write(dstDir string, task *Task) error {
	dirPath := pathpkg.Join(dstDir, d.Permalink)
@@ -255,6 +305,15 @@ func (d *Dir) write(dstDir string, task *Task) error {
		}
	}

	// Write feeds
	for name, content := range d.extraFiles {
		dstPath := pathpkg.Join(dstDir, name)
		os.MkdirAll(dirPath, 0755)
		if err := os.WriteFile(dstPath, content, 0644); err != nil {
			return err
		}
	}

	// Write subdirectories
	for _, dir := range d.Dirs {
		dir.write(dstDir, task)
diff --git a/docs/kiln.1.scd b/docs/kiln.1.scd
index dbb4c8a..1923995 100644
--- a/docs/kiln.1.scd
+++ b/docs/kiln.1.scd
@@ -187,20 +187,31 @@ site URLs may be specified for sites that are available at multiple locations.

## FEEDS

Feeds can be specified in the [feeds] table of the configuration file. Keys
denote the path to the feed directory and values denote the title of the feed.

Feeds are written to the output directory plus the feed directory plus
"atom.xml".
Feeds can be specified in [[tasks.feeds]] for every task needing them.

Example feed configuration:

	```
	# This will generate a feed which will be written to public/blog/atom.xml
	[feeds]
	"/blog/" = "My blog"
	[[tasks.feeds]]
	input_dir = "blog"
	title = "My Blog"
	template = "atom.xml"
	output = "blog/atom.xml"
	```

*input_dir*
	the content folder for which the feed will be generated

*title*
	the title of the feed, accessible via {{ .Title }} in the template

*template*
	the template to use for the feed

*output*
	the absolute path for the rendered feed

## PERMALINKS

Permalinks can be used to rewrite page paths. Permalinks are specified in the
diff --git a/main.go b/main.go
index b081099..30e5c26 100644
--- a/main.go
+++ b/main.go
@@ -71,7 +71,7 @@ func (site *Site) run() error {

func (s *Site) runTask(task *Task) error {
	// Read content
	s.root = &Dir{Permalink: "/"}
	s.root = &Dir{Permalink: "/", path: ""}
	if err := s.root.read("content", task, s); err != nil {
		return err
	}
diff --git a/site.go b/site.go
index 925ece7..189a9f7 100644
--- a/site.go
+++ b/site.go
@@ -2,7 +2,10 @@ package main

import (
	"fmt"
	"log"
	"os"
	"path"
	"strings"
	"text/template"

	"github.com/pelletier/go-toml"
@@ -31,6 +34,14 @@ type Task struct {
	StaticDir   string            `toml:"static_dir"`  // static file directory
	OutputDir   string            `toml:"output_dir"`  // output directory
	UglyURLs    bool              `toml:"ugly_urls"`   // whether to use ugly URLs
	Feeds       []Feed            `toml:"feeds"`
}

type Feed struct {
	InputDir string `toml:"input_dir"`
	Title    string `toml:"title"`
	Template string `toml:"template"`
	Output   string `toml:"output"`
}

func (t *Task) Match(ext string) bool {
@@ -80,6 +91,30 @@ func LoadSite(config string) (*Site, error) {
		return nil, err
	}

	// deprecate [feeds]
	if len(site.Feeds) > 0 {
		log.Println("The [feeds] configuration is deprecated")
		for _, task := range site.Tasks {
			if len(task.Feeds) > 0 {
				log.Println("and can't be used along [[tasks.feeds]]")
				return nil, fmt.Errorf("please remove (or rename) the [feeds] category")
			}
		}
		log.Println("Please replace it with [[tasks.feeds]] in every task needing feeds:")
		for permalink, title := range site.Feeds {
			dir := strings.Trim(permalink, "/")
			output := path.Join(dir, "atom.xml")
			fmt.Fprintf(log.Writer(), `[[tasks.feeds]]
input_dir = %q
title = %q
template = "atom.xml"
output = %q

`, dir, title, output)
		}
		log.Println("You will also need to change .Entries to .Pages in \"atom.xml\"")
	}

	return site, nil
}

diff --git a/templates/_default/atom.xml b/templates/_default/atom.xml
index 89cdd2a..553da4b 100644
--- a/templates/_default/atom.xml
+++ b/templates/_default/atom.xml
@@ -4,7 +4,7 @@
<title>{{ .Title }}</title>
<updated>{{ .Updated.Format "2006-01-02T15:04:05Z07:00" }}</updated>
<link href="{{ index site.URLs 0 | safeURL }}{{ .Permalink }}" rel="alternate"/>
{{ range .Entries }}<entry>
{{ range .Pages }}<entry>
	<id>{{ index site.URLs 0 }}{{ .Permalink }}</id>
	<title>{{ .Title }}</title>
	<updated>{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}</updated>
-- 
2.33.0
Thanks!

To git@git.sr.ht:~adnano/kiln
   508e0c2..66f25b0  master -> master