Skip to content

Commit

Permalink
TOC builder function for documentation
Browse files Browse the repository at this point in the history
Update documentation procedures

Fixes (links from relative to 'absolute')
  • Loading branch information
systemcrash committed Mar 18, 2023
1 parent ad2f028 commit 4f46b31
Show file tree
Hide file tree
Showing 147 changed files with 476 additions and 292 deletions.
175 changes: 175 additions & 0 deletions build/generate/docuTOC.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package main

import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)

const (
startMarker = "<!-- TOC start -->\n"
endMarker = "<!-- TOC end -->"
)

func generateDocuTOC(folderPath string) error {
if folderPath == "" {
return fmt.Errorf("Empty docutoc path")
}

// Find all the markdown files in the specified folder and its subfolders.
markdownFiles, err := findMarkdownFiles(folderPath)
if err != nil {
return err
}

//First sort by folders, then by filename.
sort.SliceStable(markdownFiles, func(i, j int) bool {
if filepath.Dir(markdownFiles[i]) == filepath.Dir(markdownFiles[j]) {
return strings.ToLower(markdownFiles[i]) < strings.ToLower(markdownFiles[j])
} else {
return filepath.Dir(strings.ToLower(markdownFiles[i])) < filepath.Dir(strings.ToLower(markdownFiles[j]))
}
})

// Create the table of contents.
toc := generateTableOfContents(folderPath, markdownFiles)

err = replaceTextBetweenMarkers(filepath.Join(folderPath, "SUMMARY.md"), startMarker, endMarker, toc)
if err != nil {
return err
}

return nil
}

// func stringInSlice(a string, list []string) bool {
// for _, b := range list {
// if b == a {
// return true
// }
// }
// return false
// }

func generateTableOfContents(folderPath string, markdownFiles []string) string {
var toc strings.Builder
currentFolder := ""

// skip over these root entries (dont print these "#" headings)
rootFolderExceptions := []string{
"documentation",
}

// dont print these folder names as bullets (which lack a link)
folderExceptions := []string{
"providers",
}
// dont print these file names as bullets
fileExceptions := []string{
"js.md",
"index.md",
"summary.md",
"ignore-me.md",
}

for _, file := range markdownFiles {
//depthCount is folder depth for toc indentation, minus one for docu folder
depthCount := strings.Count(file, string(filepath.Separator)) - 1
filename := filepath.Base(file)

fileFolder := filepath.Dir(file)
if fileFolder != currentFolder {
// we are in a new folder

// hop over these entries altogether
if stringInSlice(strings.ToLower(fileFolder), rootFolderExceptions) {
continue
}
currentFolder = fileFolder
folderName := filepath.Base(currentFolder)

// if we're in an "exception" folder, deeper than a heading "#", skip printing it
// this has the effect of putting subentries under an entry that is already a link,
// without printing an entry to represent the folder name that is not a link
// e.g. provider md files are all links, under the provider.md file which is a link
if stringInSlice(strings.ToLower(folderName), folderExceptions) && depthCount > 1 {
continue
} else {
if depthCount > 1 {
// if we're deeper in heirarchy, print an indented bullet "*" to add to bullet heirarchy
toc.WriteString(strings.Repeat(" ", depthCount-1) + "* ")
} else {
// If we're in folder root, just print an unindented heading "#"
toc.WriteString("\n\n## ")
}
// Captalize folder names, replace underscores with spaces for # headings
toc.WriteString(strings.Title(strings.ReplaceAll(folderName, "_", " ")) + "\n")
}
}
//if the file is an exception listed above, skip it.
if stringInSlice(strings.ToLower(filename), fileExceptions) {
continue
}

// naming exceptions - function names shall retain "_"
displayfilename := strings.TrimSuffix(filename, filepath.Ext(filename))
if !strings.Contains(file, "functions") {
displayfilename = strings.Title(strings.ReplaceAll(displayfilename, "_", " "))
}

// print the filename as a bullet, and as a [link](hyperlink)
toc.WriteString(strings.Repeat(" ", depthCount))
toc.WriteString("* [" + displayfilename + "](" + filepath.Join(".", strings.ReplaceAll(file, folderPath, "")) + ")\n")
}
return toc.String()
}

// replaceTextBetweenMarkers inserts the generated table of contents between the two markers in the specified markdown file.
func replaceTextBetweenMarkers(targetFile, startMarker, endMarker, newcontent string) error {
// Read the contents of the markdown file into memory.
input, err := os.ReadFile(targetFile)
if err != nil {
return err
}

// Find the starting and ending positions of the table of contents markers.
startPos := strings.Index(string(input), startMarker)
if startPos == -1 {
return fmt.Errorf("could not find start marker %q in file %q", startMarker, targetFile)
}
endPos := strings.Index(string(input), endMarker)
if endPos == -1 {
return fmt.Errorf("could not find end marker %q in file %q", endMarker, targetFile)
}

// Construct the new contents of the markdown file with the updated table of contents.
output := string(input[:startPos+len(startMarker)]) + newcontent + string(input[endPos:])

// Write the updated contents to the markdown file.
err = os.WriteFile(targetFile, []byte(output), 0644)
if err != nil {
return err
}

return nil
}

// findMarkdownFiles returns a list of all the markdown files in the specified folder and its subfolders.
func findMarkdownFiles(folderPath string) ([]string, error) {
markdownFiles := make([]string, 0)
err := filepath.Walk(folderPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && filepath.Ext(path) == ".md" {
markdownFiles = append(markdownFiles, path)
}
return nil
})
if err != nil {
return nil, err
}
return markdownFiles, err
}
24 changes: 12 additions & 12 deletions build/generate/featureMatrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,20 @@ func matrixData() *FeatureMatrix {
OfficialSupport = "Official Support" // vs. community supported
ProviderDNSProvider = "DNS Provider"
ProviderRegistrar = "Registrar"
DomainModifierAlias = "[`ALIAS`](functions/domain/ALIAS.md)"
DomainModifierCaa = "[`CAA`](functions/domain/CAA.md)"
DomainModifierDnssec = "[`AUTODNSSEC`](functions/domain/AUTODNSSEC_ON.md)"
DomainModifierLoc = "[`LOC`](functions/domain/LOC.md)"
DomainModifierNaptr = "[`NAPTR`](functions/domain/NAPTR.md)"
DomainModifierPtr = "[`PTR`](functions/domain/PTR.md)"
DomainModifierSoa = "[`SOA`](functions/domain/SOA.md)"
DomainModifierSrv = "[`SRV`](functions/domain/SRV.md)"
DomainModifierSshfp = "[`SSHFP`](functions/domain/SSHFP.md)"
DomainModifierTlsa = "[`TLSA`](functions/domain/TLSA.md)"
DomainModifierDs = "[`DS`](functions/domain/DS.md)"
DomainModifierAlias = "[`ALIAS`](02_language_reference/domain_modifier_functions/ALIAS.md)"
DomainModifierCaa = "[`CAA`](02_language_reference/domain_modifier_functions/CAA.md)"
DomainModifierDnssec = "[`AUTODNSSEC`](02_language_reference/domain_modifier_functions/AUTODNSSEC_ON.md)"
DomainModifierLoc = "[`LOC`](02_language_reference/domain_modifier_functions/LOC.md)"
DomainModifierNaptr = "[`NAPTR`](02_language_reference/domain_modifier_functions/NAPTR.md)"
DomainModifierPtr = "[`PTR`](02_language_reference/domain_modifier_functions/PTR.md)"
DomainModifierSoa = "[`SOA`](02_language_reference/domain_modifier_functions/SOA.md)"
DomainModifierSrv = "[`SRV`](02_language_reference/domain_modifier_functions/SRV.md)"
DomainModifierSshfp = "[`SSHFP`](02_language_reference/domain_modifier_functions/SSHFP.md)"
DomainModifierTlsa = "[`TLSA`](02_language_reference/domain_modifier_functions/TLSA.md)"
DomainModifierDs = "[`DS`](02_language_reference/domain_modifier_functions/DS.md)"
DualHost = "dual host"
CreateDomains = "create-domains"
NoPurge = "[`NO_PURGE`](functions/domain/NO_PURGE.md)"
NoPurge = "[`NO_PURGE`](02_language_reference/domain_modifier_functions/NO_PURGE.md)"
GetZones = "get-zones"
)

Expand Down
39 changes: 34 additions & 5 deletions build/generate/functionTypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,45 @@ func parseFrontMatter(content string) (map[string]interface{}, string, error) {
}

var returnTypes = map[string]string{
"domain": "DomainModifier",
"global": "void",
"record": "RecordModifier",
"domain_modifier_functions": "DomainModifier",
"top_level_functions": "void",
"record_modifier_functions": "RecordModifier",
}

func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}

func generateFunctionTypes() (string, error) {
funcs := []Function{}
folderExceptions := []string{
".DS_Store",
"js.md",
}
fileExceptions := []string{
".DS_Store",
"js.md",
// appears in TOC, but not needed here. it's just in the same folder.
"Why_CNAME,_MX,_S_targets_require_the_final_\".\".md",
}

srcRoot := join("documentation", "functions")
srcRoot := join("documentation", "02_language_reference")
types, err := os.ReadDir(srcRoot)
if err != nil {
return "", err
}
for _, t := range types {
if stringInSlice(t.Name(), folderExceptions) {
continue
}
if stringInSlice(t.Name(), fileExceptions) {
continue
}
if !t.IsDir() {
return "", errors.New("not a directory: " + join(srcRoot, t.Name()))
}
Expand All @@ -73,9 +98,13 @@ func generateFunctionTypes() (string, error) {
}

for _, f := range funcNames {
if stringInSlice(f.Name(), fileExceptions) {
continue
}
fPath := join(tPath, f.Name())
if f.IsDir() {
return "", errors.New("not a file: " + fPath)
// return "", errors.New("not a file: " + fPath)
continue
}
// println("Processing", fPath)
content, err := os.ReadFile(fPath)
Expand Down
3 changes: 3 additions & 0 deletions build/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ func main() {
if err := generateDTSFile(funcs); err != nil {
log.Fatal(err)
}
if err := generateDocuTOC("documentation"); err != nil {
log.Fatal(err)
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ parameter_types:

A adds an A record To a domain. The name should be the relative label for the record. Use `@` for the domain apex.

The address should be an ip address, either a string, or a numeric value obtained via [IP](../global/IP.md).
The address should be an ip address, either a string, or a numeric value obtained via [IP](../top_level_functions/IP.md).

Modifiers can be any number of [record modifiers](https://docs.dnscontrol.org/language-reference/record-modifiers) or JSON objects, which will be merged into the record's metadata.

Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ D("example.com", REGISTRAR, DnsProvider("GCLOUD"),
```
{% endcode %}

DNSControl contains a [`CAA_BUILDER`](../record/CAA_BUILDER.md) which can be used to simply create `CAA()` records for your domains. Instead of creating each CAA record individually, you can simply configure your report mail address, the authorized certificate authorities and the builder cares about the rest.
DNSControl contains a [`CAA_BUILDER`](../record_modifier_functions/CAA_BUILDER.md) which can be used to simply create `CAA()` records for your domains. Instead of creating each CAA record individually, you can simply configure your report mail address, the authorized certificate authorities and the builder cares about the rest.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: DefaultTTL
parameters:
- ttl
parameter_types:
ttl: Duration
---

DefaultTTL sets the TTL for all subsequent records following it in a domain that do not explicitly set one with [`TTL`](/documentation/02_language_reference/record_modifier_functions/TTL.md). If neither `DefaultTTL` or `TTL` exist for a record,
the record will inherit the DNSControl global internal default of 300 seconds. See also [`DEFAULTS`](/documentation/02_language_reference/top_level_functions/DEFAULTS.md) to override the internal defaults.

NS records are currently a special case, and do not inherit from `DefaultTTL`. See [`NAMESERVER_TTL`](/documentation/02_language_reference/domain_modifier_functions/NAMESERVER_TTL.md) to set a default TTL for all NS records.


{% code title="dnsconfig.js" %}
```javascript
D('example.com', REGISTRAR, DnsProvider('R53'),
DefaultTTL("4h"),
A('@','1.2.3.4'), // uses default
A('foo', '2.3.4.5', TTL(600)) // overrides default
);
```
{% endcode %}

The DefaultTTL duration is the same format as [`TTL`](/documentation/02_language_reference/record_modifier_functions/TTL.md), an integer number of seconds
or a string with a unit such as `'4d'`.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ parameter_types:
---

DnsProvider indicates that the specified provider should be used to manage
records for this domain. The name must match the name used with [NewDnsProvider](../global/NewDnsProvider.md).
records for this domain. The name must match the name used with [NewDnsProvider](../top_level_functions/NewDnsProvider.md).

The nsCount parameter determines how the nameservers will be managed from this provider.

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ the LOC record type will supply defaults where values were absent on DNS import.
One must supply the `LOC()` js helper all parameters. If that seems like too
much work, see also helper functions:

* [`LOC_BUILDER_DD({})`](../record/LOC_BUILDER_DD.md) - build a `LOC` by supplying only **d**ecimal **d**egrees.
* [`LOC_BUILDER_DMS_STR({})`](../record/LOC_BUILDER_DMS_STR.md) - accepts DMS 33°51′31″S 151°12′51″E
* [`LOC_BUILDER_DMM_STR({})`](../record/LOC_BUILDER_DMM_STR.md) - accepts DMM 25.24°S 153.15°E
* [`LOC_BUILDER_STR({})`](../record/LOC_BUILDER_STR.md) - tries the cooordinate string in all `LOC_BUILDER_DM*_STR()` functions until one works
* [`LOC_BUILDER_DD({})`](../record_modifier_functions/LOC_BUILDER_DD.md) - build a `LOC` by supplying only **d**ecimal **d**egrees.
* [`LOC_BUILDER_DMS_STR({})`](../record_modifier_functions/LOC_BUILDER_DMS_STR.md) - accepts DMS 33°51′31″S 151°12′51″E
* [`LOC_BUILDER_DMM_STR({})`](../record_modifier_functions/LOC_BUILDER_DMM_STR.md) - accepts DMM 25.24°S 153.15°E
* [`LOC_BUILDER_STR({})`](../record_modifier_functions/LOC_BUILDER_STR.md) - tries the cooordinate string in all `LOC_BUILDER_DM*_STR()` functions until one works

## Format ##

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ parameter_types:

NAMESERVER_TTL sets the TTL on the domain apex NS RRs defined by [`NAMESERVER`](NAMESERVER.md).

The value can be an integer or a string. See [`TTL`](../record/TTL.md) for examples.
The value can be an integer or a string. See [`TTL`](/documentation/02_language_reference/record_modifier_functions/TTL.md) for examples.

{% code title="dnsconfig.js" %}
```javascript
Expand All @@ -36,4 +36,4 @@ D('example.com', REGISTRAR, DnsProvider('xyz'),
```
{% endcode %}

To apply a default TTL to all other record types, see [`DefaultTTL`](../domain/DefaultTTL.md)
To apply a default TTL to all other record types, see [`DefaultTTL`](/documentation/02_language_reference/domain_modifier_functions/DefaultTTL.md)
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ are all equivalent:
* `PTR('4.3',` // Assuming the domain is `2.1.in-addr.arpa`

All magic is RFC2317-aware. We use the first format listed in the
RFC for both [`REV()`](../global/REV.md) and `PTR()`. The format is
RFC for both [`REV()`](../top_level_functions/REV.md) and `PTR()`. The format is
`FIRST/MASK.C.B.A.in-addr.arpa` where `FIRST` is the first IP address
of the zone, `MASK` is the netmask of the zone (25-31 inclusive),
and A, B, C are the first 3 octets of the IP address. For example
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ double quotes, back-ticks, or other chars.

#### How can you tell if a provider will support a particular `TXT()` record?

Include the `TXT()` record in a [`D()`](../global/D.md) as usual, along
Include the `TXT()` record in a [`D()`](../top_level_functions/D.md) as usual, along
with the `DnsProvider()` for that provider. Run `dnscontrol check` to
see if any errors are produced. The check command does not talk to
the provider's API, thus permitting you to do this without having an
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ provider: ROUTE53

`R53_ALIAS` is a Route53 specific virtual record type that points a record at either another record or an AWS entity (like a Cloudfront distribution, an ELB, etc...). It is analogous to a `CNAME`, but is usually resolved at request-time and served as an `A` record. Unlike `CNAME` records, `ALIAS` records can be used at the zone apex (`@`)

Unlike the regular [`ALIAS`](ALIAS.md) directive, `R53_ALIAS` is only supported on Route53. Attempting to use `R53_ALIAS` on another provider than Route53 will result in an error.
Unlike the regular [`ALIAS`](../../ALIAS.md) directive, `R53_ALIAS` is only supported on Route53. Attempting to use `R53_ALIAS` on another provider than Route53 will result in an error.

The name should be the relative label for the domain.

Expand Down
Empty file.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ parameter_types:
---

DNSControl contains a `CAA_BUILDER` which can be used to simply create
[`CAA()`](../domain/CAA.md) records for your domains. Instead of creating each [`CAA()`](../domain/CAA.md) record
`CAA` records for your domains. Instead of creating each `CAA` record
individually, you can simply configure your report mail address, the
authorized certificate authorities and the builder cares about the rest.

Expand Down
Loading

0 comments on commit 4f46b31

Please sign in to comment.