mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-24 16:41:35 -04:00
Merge pull request #5903 from kobergj/AutomateMDCreation
Automate Creation of `_index.md` Files
This commit is contained in:
5
changelog/unreleased/automate-md-creation.md
Normal file
5
changelog/unreleased/automate-md-creation.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Enhancement: Automate md creation
|
||||
|
||||
Automatically create `_index.md` files from the services `README.md`
|
||||
|
||||
https://github.com/owncloud/ocis/pull/5901
|
||||
17
docs/helpers/index.tmpl
Normal file
17
docs/helpers/index.tmpl
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: {{ .ServiceName }}
|
||||
date: {{ .CreationTime }}
|
||||
weight: 20
|
||||
geekdocRepo: https://github.com/owncloud/ocis
|
||||
geekdocEditPath: edit/master/docs/services/{{ .service }}
|
||||
geekdocFilePath: _index.md
|
||||
geekdocCollapseSection: true
|
||||
---
|
||||
|
||||
## Abstract
|
||||
|
||||
{{ .Abstract }}
|
||||
## Table of Contents
|
||||
|
||||
{{ .TocTree }}
|
||||
{{ .Content }}
|
||||
@@ -4,4 +4,5 @@ func main() {
|
||||
RenderTemplates()
|
||||
GetRogueEnvs()
|
||||
RenderGlobalVarsTemplate()
|
||||
GenerateServiceIndexMarkdowns()
|
||||
}
|
||||
|
||||
71
docs/helpers/markdowncreation.go
Normal file
71
docs/helpers/markdowncreation.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/markdown"
|
||||
)
|
||||
|
||||
var _configMarkdown = `{{< include file="services/_includes/%s-config-example.yaml" language="yaml" >}}
|
||||
|
||||
{{< include file="services/_includes/%s_configvars.md" >}}
|
||||
`
|
||||
|
||||
// GenerateServiceIndexMarkdowns generates the _index.md files for the dev docu
|
||||
func GenerateServiceIndexMarkdowns() {
|
||||
paths, err := filepath.Glob("../../services/*/README.md")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, p := range paths {
|
||||
service := filepath.Base(filepath.Dir(p))
|
||||
if err := generateMarkdown(p, service); err != nil {
|
||||
fmt.Printf("error generating markdown for %s: %s\n", service, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateMarkdown(filepath string, servicename string) error {
|
||||
f, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
md := markdown.NewMD(f)
|
||||
if len(md.Headings) == 0 || md.Headings[0].Level != 1 {
|
||||
return errors.New("readme has invalid format")
|
||||
}
|
||||
|
||||
// we don't need the main title, we add in our template
|
||||
head := md.Headings[0]
|
||||
md.Headings = md.Headings[1:]
|
||||
md.Headings = append(md.Headings, markdown.Heading{
|
||||
Level: 2,
|
||||
Header: "Example Yaml Config",
|
||||
Content: fmt.Sprintf(_configMarkdown, servicename, servicename),
|
||||
})
|
||||
|
||||
tpl := template.Must(template.ParseFiles("index.tmpl"))
|
||||
b := bytes.NewBuffer(nil)
|
||||
if err := tpl.Execute(b, map[string]interface{}{
|
||||
"ServiceName": head.Header,
|
||||
"CreationTime": time.Now().Format(time.RFC3339Nano),
|
||||
"service": servicename,
|
||||
"Abstract": head.Content,
|
||||
"TocTree": md.TocString(),
|
||||
"Content": md.String(),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetFile := fmt.Sprintf("../../docs/services/%s/_index.md", servicename)
|
||||
return os.WriteFile(targetFile, b.Bytes(), os.ModePerm)
|
||||
}
|
||||
137
ocis-pkg/markdown/markdown.go
Normal file
137
ocis-pkg/markdown/markdown.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// Package markdown allows reading and editing Markdown files
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Heading represents a markdown Heading
|
||||
type Heading struct {
|
||||
Level int // the level of the heading. 1 means it's the H1
|
||||
Content string // the text of the heading
|
||||
Header string // the heading itself
|
||||
}
|
||||
|
||||
// MD represents a markdown file
|
||||
type MD struct {
|
||||
Headings []Heading
|
||||
}
|
||||
|
||||
// Bytes returns the markdown as []bytes, ignoring errors
|
||||
func (md MD) Bytes() []byte {
|
||||
var b bytes.Buffer
|
||||
_, _ = md.WriteContent(&b)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// String returns the markdown as string, ignoring errors
|
||||
func (md MD) String() string {
|
||||
var b strings.Builder
|
||||
_, _ = md.WriteContent(&b)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// TocBytes returns the table of contents as []byte, ignoring errors
|
||||
func (md MD) TocBytes() []byte {
|
||||
var b bytes.Buffer
|
||||
_, _ = md.WriteToc(&b)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// TocString returns the table of contents as string, ignoring errors
|
||||
func (md MD) TocString() string {
|
||||
var b strings.Builder
|
||||
_, _ = md.WriteToc(&b)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// WriteContent writes the MDs content to the given writer
|
||||
func (md MD) WriteContent(w io.Writer) (int64, error) {
|
||||
max, written := len(md.Headings), int64(0)
|
||||
write := func(s string) error {
|
||||
n, err := w.Write([]byte(s))
|
||||
written += int64(n)
|
||||
return err
|
||||
}
|
||||
for i, h := range md.Headings {
|
||||
if err := write(strings.Repeat("#", h.Level) + " " + h.Header + "\n"); err != nil {
|
||||
return written, err
|
||||
}
|
||||
if err := write("\n"); err != nil {
|
||||
return written, err
|
||||
}
|
||||
if len(h.Content) > 0 {
|
||||
if err := write(h.Content); err != nil {
|
||||
return written, err
|
||||
}
|
||||
if i < max-1 {
|
||||
if err := write("\n"); err != nil {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// WriteToc writes the table of contents to the given writer
|
||||
func (md MD) WriteToc(w io.Writer) (int64, error) {
|
||||
var written int64
|
||||
for _, h := range md.Headings {
|
||||
if h.Level == 1 {
|
||||
// main title not in toc
|
||||
continue
|
||||
}
|
||||
link := fmt.Sprintf("#%s", strings.ToLower(strings.Replace(h.Header, " ", "-", -1)))
|
||||
s := fmt.Sprintf("%s* [%s](%s)\n", strings.Repeat(" ", h.Level-2), h.Header, link)
|
||||
n, err := w.Write([]byte(s))
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
written += int64(n)
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// NewMD parses a new Markdown
|
||||
func NewMD(b []byte) MD {
|
||||
var (
|
||||
md MD
|
||||
heading Heading
|
||||
content strings.Builder
|
||||
)
|
||||
sendHeading := func() {
|
||||
if heading.Header != "" {
|
||||
heading.Content = content.String()
|
||||
md.Headings = append(md.Headings, heading)
|
||||
content = strings.Builder{}
|
||||
}
|
||||
}
|
||||
parts := strings.Split(string(b), "\n")
|
||||
for _, p := range parts {
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
if p[:1] == "#" { // this is a header
|
||||
sendHeading()
|
||||
heading = headingFromString(p)
|
||||
} else {
|
||||
// readd lost "\n"
|
||||
_, _ = content.WriteString(p + "\n")
|
||||
}
|
||||
}
|
||||
sendHeading()
|
||||
return md
|
||||
}
|
||||
|
||||
func headingFromString(s string) Heading {
|
||||
i := strings.LastIndex(s, "#")
|
||||
levs, con := s[:i+1], s[i+1:]
|
||||
return Heading{
|
||||
Level: len(levs),
|
||||
Header: strings.TrimPrefix(con, " "),
|
||||
}
|
||||
}
|
||||
13
ocis-pkg/markdown/markdown_suite_test.go
Normal file
13
ocis-pkg/markdown/markdown_suite_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestSearch(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Markdown Suite")
|
||||
}
|
||||
48
ocis-pkg/markdown/markdown_test.go
Normal file
48
ocis-pkg/markdown/markdown_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package markdown
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var (
|
||||
SmallMarkdown = `# Title
|
||||
|
||||
some abstract description
|
||||
|
||||
## SubTitle 1
|
||||
|
||||
subtitle one description
|
||||
|
||||
## SubTitle 2
|
||||
|
||||
subtitle two description
|
||||
|
||||
### Subpoint to SubTitle 2
|
||||
|
||||
description to subpoint
|
||||
`
|
||||
SmallMD = MD{
|
||||
Headings: []Heading{
|
||||
{Level: 1, Header: "Title", Content: "some abstract description\n"},
|
||||
{Level: 2, Header: "SubTitle 1", Content: "subtitle one description\n"},
|
||||
{Level: 2, Header: "SubTitle 2", Content: "subtitle two description\n"},
|
||||
{Level: 3, Header: "Subpoint to SubTitle 2", Content: "description to subpoint\n"},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var _ = Describe("TestMarkdown", func() {
|
||||
DescribeTable("Conversion works both ways",
|
||||
func(mdfile string, expectedMD MD) {
|
||||
md := NewMD([]byte(mdfile))
|
||||
|
||||
Expect(len(md.Headings)).To(Equal(len(expectedMD.Headings)))
|
||||
for i, h := range md.Headings {
|
||||
Expect(h).To(Equal(expectedMD.Headings[i]))
|
||||
}
|
||||
Expect(md.String()).To(Equal(mdfile))
|
||||
},
|
||||
Entry("converts a small markdown", SmallMarkdown, SmallMD),
|
||||
)
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
#### Notification service
|
||||
# Notification service
|
||||
|
||||
The notification service is responsible for sending emails to users informing them about events that happened. To do this it hooks into the event system and listens for certain events that the users need to be informed about.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user