2019-03-08 20:47:06 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
//go:generate go run .
|
|
|
|
|
|
|
|
import (
|
2020-03-09 10:43:27 +02:00
|
|
|
"bufio"
|
2019-03-08 20:47:06 +02:00
|
|
|
"bytes"
|
2020-03-09 10:43:27 +02:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2019-03-08 20:47:06 +02:00
|
|
|
"go/format"
|
2020-03-09 10:43:27 +02:00
|
|
|
"io"
|
2019-03-08 20:47:06 +02:00
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2020-03-09 10:43:27 +02:00
|
|
|
"sort"
|
2019-03-08 20:47:06 +02:00
|
|
|
"strings"
|
|
|
|
"text/template"
|
|
|
|
|
|
|
|
"github.com/BurntSushi/toml"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
root = "../../"
|
|
|
|
dnsPackage = root + "providers/dns"
|
|
|
|
mdTemplate = root + "internal/dnsdocs/dns.md.tmpl"
|
|
|
|
cliTemplate = root + "internal/dnsdocs/dns.go.tmpl"
|
|
|
|
cliOutput = root + "cmd/zz_gen_cmd_dnshelp.go"
|
|
|
|
docOutput = root + "docs/content/dns"
|
2020-03-09 10:43:27 +02:00
|
|
|
readmePath = root + "README.md"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
startLine = "<!-- START DNS PROVIDERS LIST -->"
|
|
|
|
endLine = "<!-- END DNS PROVIDERS LIST -->"
|
2019-03-08 20:47:06 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type Model struct {
|
|
|
|
Name string // Real name of the DNS provider
|
|
|
|
Code string // DNS code
|
2019-04-17 21:32:38 +02:00
|
|
|
Since string // First lego version
|
2019-03-08 20:47:06 +02:00
|
|
|
URL string // DNS provider URL
|
|
|
|
Description string // Provider summary
|
|
|
|
Example string // CLI example
|
|
|
|
Configuration *Configuration // Environment variables
|
|
|
|
Links *Links // Links
|
|
|
|
Additional string // Extra documentation
|
|
|
|
GeneratedFrom string // Source file
|
|
|
|
}
|
|
|
|
|
|
|
|
type Configuration struct {
|
|
|
|
Credentials map[string]string
|
|
|
|
Additional map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Links struct {
|
|
|
|
API string
|
|
|
|
GoClient string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Providers struct {
|
|
|
|
Providers []Model
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
models := &Providers{}
|
|
|
|
|
|
|
|
err := filepath.Walk(dnsPackage, walker(models))
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate CLI help
|
|
|
|
err = generateCLIHelp(models)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2020-03-09 10:43:27 +02:00
|
|
|
|
|
|
|
// generate README.md
|
|
|
|
err = generateReadMe(models)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf("Documentation for %d DNS providers has been generated.\n", len(models.Providers)+1)
|
2019-03-08 20:47:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func walker(prs *Providers) func(string, os.FileInfo, error) error {
|
|
|
|
return func(path string, _ os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if filepath.Ext(path) == ".toml" {
|
|
|
|
m := Model{}
|
|
|
|
|
|
|
|
m.GeneratedFrom, err = filepath.Rel(root, path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := toml.DecodeFile(path, &m)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
prs.Providers = append(prs.Providers, m)
|
|
|
|
|
|
|
|
// generate documentation
|
|
|
|
return generateDocumentation(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateDocumentation(m Model) error {
|
|
|
|
filename := filepath.Join(docOutput, "zz_gen_"+m.Code+".md")
|
|
|
|
|
|
|
|
file, err := os.Create(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return template.Must(template.ParseFiles(mdTemplate)).Execute(file, m)
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateCLIHelp(models *Providers) error {
|
2021-11-03 21:39:12 +02:00
|
|
|
filename := filepath.Clean(cliOutput)
|
2019-03-08 20:47:06 +02:00
|
|
|
|
|
|
|
file, err := os.Create(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
tlt := template.New(filepath.Base(cliTemplate)).Funcs(map[string]interface{}{
|
|
|
|
"safe": func(src string) string {
|
|
|
|
return strings.ReplaceAll(src, "`", "'")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
b := &bytes.Buffer{}
|
|
|
|
err = template.Must(tlt.ParseFiles(cliTemplate)).Execute(b, models)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// gofmt
|
|
|
|
source, err := format.Source(b.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = file.Write(source)
|
|
|
|
return err
|
|
|
|
}
|
2020-03-09 10:43:27 +02:00
|
|
|
|
|
|
|
func generateReadMe(models *Providers) error {
|
|
|
|
max, lines := extractTableData(models)
|
|
|
|
|
|
|
|
file, err := os.Open(readmePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() { _ = file.Close() }()
|
|
|
|
|
|
|
|
var skip bool
|
|
|
|
|
|
|
|
buffer := bytes.NewBufferString("")
|
|
|
|
|
|
|
|
fileScanner := bufio.NewScanner(file)
|
|
|
|
for fileScanner.Scan() {
|
|
|
|
text := fileScanner.Text()
|
|
|
|
|
|
|
|
if text == startLine {
|
|
|
|
_, _ = fmt.Fprintln(buffer, text)
|
|
|
|
err = writeDNSTable(buffer, lines, max)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
skip = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if text == endLine {
|
|
|
|
skip = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if skip {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
_, _ = fmt.Fprintln(buffer, text)
|
|
|
|
}
|
|
|
|
|
|
|
|
if fileScanner.Err() != nil {
|
|
|
|
return fileScanner.Err()
|
|
|
|
}
|
|
|
|
|
|
|
|
if skip {
|
|
|
|
return errors.New("missing end tag")
|
|
|
|
}
|
|
|
|
|
2021-08-25 11:44:11 +02:00
|
|
|
return os.WriteFile(readmePath, buffer.Bytes(), 0o666)
|
2020-03-09 10:43:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func extractTableData(models *Providers) (int, [][]string) {
|
|
|
|
readmePattern := "[%s](https://go-acme.github.io/lego/dns/%s/)"
|
|
|
|
|
|
|
|
items := []string{fmt.Sprintf(readmePattern, "Manual", "manual")}
|
|
|
|
|
|
|
|
var max int
|
|
|
|
|
|
|
|
for _, pvd := range models.Providers {
|
|
|
|
item := fmt.Sprintf(readmePattern, strings.ReplaceAll(pvd.Name, "|", "/"), pvd.Code)
|
|
|
|
items = append(items, item)
|
|
|
|
|
|
|
|
if max < len(item) {
|
|
|
|
max = len(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const nbCol = 4
|
|
|
|
|
|
|
|
sort.Slice(items, func(i, j int) bool {
|
|
|
|
return strings.ToLower(items[i]) < strings.ToLower(items[j])
|
|
|
|
})
|
|
|
|
|
|
|
|
var lines [][]string
|
|
|
|
var line []string
|
|
|
|
|
|
|
|
for i, item := range items {
|
|
|
|
switch {
|
|
|
|
case len(line) == nbCol:
|
|
|
|
lines = append(lines, line)
|
|
|
|
line = []string{item}
|
|
|
|
|
2020-03-09 11:06:13 +02:00
|
|
|
case i == len(items)-1:
|
2020-03-09 10:43:27 +02:00
|
|
|
line = append(line, item)
|
|
|
|
for j := len(line); j < nbCol; j++ {
|
|
|
|
line = append(line, "")
|
|
|
|
}
|
|
|
|
lines = append(lines, line)
|
|
|
|
|
|
|
|
default:
|
|
|
|
line = append(line, item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(line) < nbCol {
|
|
|
|
for j := len(line); j < nbCol; j++ {
|
|
|
|
line = append(line, "")
|
|
|
|
}
|
|
|
|
lines = append(lines, line)
|
|
|
|
}
|
|
|
|
|
|
|
|
return max, lines
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeDNSTable(w io.Writer, lines [][]string, size int) error {
|
|
|
|
_, err := fmt.Fprintf(w, "\n")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = fmt.Fprintf(w, "|%[1]s|%[1]s|%[1]s|%[1]s|\n", strings.Repeat(" ", size+2))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = fmt.Fprintf(w, "|%[1]s|%[1]s|%[1]s|%[1]s|\n", strings.Repeat("-", size+2))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
linePattern := fmt.Sprintf("| %%-%[1]ds | %%-%[1]ds | %%-%[1]ds | %%-%[1]ds |\n", size)
|
|
|
|
for _, line := range lines {
|
|
|
|
_, err = fmt.Fprintf(w, linePattern, line[0], line[1], line[2], line[3])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = fmt.Fprintf(w, "\n")
|
|
|
|
return err
|
|
|
|
}
|