~adnano/kiln-devel

tasks: add tasks.feeds support v1 SUPERSEDED

oliverpool: 1
 tasks: add tasks.feeds support

 4 files changed, 78 insertions(+), 4 deletions(-)
Thanks for the review.

Let me know how I should handle the following points, so that I can make a patch v2:
- feed => integrate inside feeds?
- feeds.Permalink or Dir.path?
(see the details below)
Next
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/24818/mbox | git am -3
Learn more about email & git

[PATCH] 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"
---
After our discussion on kiln-discuss, here is my attempt to support the 
[[tasks.feeds]] configuration.

A couple of things must be done after (or before) this is merged:
- update the documentation
- deprecate the current [[feeds]] config

Let me know what you think!
 config.toml                 |  9 ++++--
 dir.go                      | 63 +++++++++++++++++++++++++++++++++++++
 site.go                     |  8 +++++
 templates/_default/atom.xml |  2 +-
 4 files changed, 78 insertions(+), 4 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..9d34cc7 100644
--- a/dir.go
+++ b/dir.go
@@ -23,6 +23,8 @@ type Dir struct {
	Dirs      []*Dir
	index     *Page  // The index page.
	feed      []byte // Atom feed.

	extraFiles map[string][]byte // extra generated files
}

// Page represents a page.
@@ -151,6 +153,18 @@ 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
	for _, feed := range task.Feeds {
		if strings.Trim(d.Permalink, "/") != strings.Trim(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 +228,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 \"%s\" 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 +302,22 @@ func (d *Dir) write(dstDir string, task *Task) error {
		}
	}

	for name, content := range d.extraFiles {
		if strings.HasPrefix(name, "/") {
			// if name starts with /, put it at the root
			name = strings.TrimPrefix(name, "/")
		} else {
			// otherwise put it under d.Permalink
			name = pathpkg.Join(d.Permalink, name)
		}

		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/site.go b/site.go
index 925ece7..31dd3c2 100644
--- a/site.go
+++ b/site.go
@@ -31,6 +31,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 {
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