diff --git a/.gitbook.yaml b/.gitbook.yaml
new file mode 100644
index 000000000..b346c76c6
--- /dev/null
+++ b/.gitbook.yaml
@@ -0,0 +1 @@
+root: ./documentation/
diff --git a/README.md b/README.md
index 79c637bb7..d8a6cbaf0 100644
--- a/README.md
+++ b/README.md
@@ -104,7 +104,7 @@ Running `dnscontrol preview` will talk to the providers (here name.com as regist
Running `dnscontrol push` will make those changes with the provider and my dns records will be correctly updated.
-See [Getting Started](https://stackexchange.github.io/dnscontrol/getting-started) page on documentation site.
+See [Getting Started](https://docs.dnscontrol.org/getting-started/getting-started) page on documentation site.
## Benefits
@@ -141,7 +141,7 @@ See [Getting Started](https://stackexchange.github.io/dnscontrol/getting-started
## Installation
-DNSControl can be installed via packages for macOS, Linux and Windows, or from source code. See the [official instructions](https://stackexchange.github.io/dnscontrol/getting-started#1-install-the-software).
+DNSControl can be installed via packages for macOS, Linux and Windows, or from source code. See the [official instructions](https://docs.dnscontrol.org/getting-started/getting-started#1-install-the-software).
## Via GitHub Actions (GHA)
@@ -151,10 +151,10 @@ See [dnscontrol-action](https://github.com/koenrh/dnscontrol-action) or [gacts/i
- **Call for new volunteer maintainers for NAMECHEAP, NAMEDOTCOM, and SOFTLAYER.** These providers have no maintainer. Maintainers respond to PRs and fix bugs in a timely manner, and try to stay on top of protocol changes.
- **ACME/Let's Encrypt support is frozen and will be removed after December 31, 2022.** The `get-certs` command (renews certs via Let's Encrypt) has no maintainer. There are other projects that do a better job. If you don't use this feature, please do not start. If you do use this feature, please plan on migrating to something else. See discussion in [issues/1400](https://github.com/StackExchange/dnscontrol/issues/1400)
-- **get-zones syntax changes in v3.16** Starting in v3.16, the command line arguments for `dnscontrol get-zones` changes. For backwards compatibility change `provider` to `-`. See documentation for details.
+- **get-zones syntax changes in v3.16** Starting in [v3.16](v316.md), the command line arguments for `dnscontrol get-zones` changes. For backwards compatibility change `provider` to `-`. See documentation for details.
## More info at our website
The website: [https://stackexchange.github.io/dnscontrol/](https://stackexchange.github.io/dnscontrol/)
-The getting started guide: [https://stackexchange.github.io/dnscontrol/getting-started](https://stackexchange.github.io/dnscontrol/getting-started)
+The getting started guide: [https://docs.dnscontrol.org/getting-started/getting-started](https://docs.dnscontrol.org/getting-started/getting-started)
diff --git a/build/generate/featureMatrix.go b/build/generate/featureMatrix.go
index 4df4c6df0..c7dd32bb5 100644
--- a/build/generate/featureMatrix.go
+++ b/build/generate/featureMatrix.go
@@ -1,167 +1,281 @@
package main
import (
- "bytes"
- "html/template"
- "os"
- "sort"
-
+ "fmt"
"github.com/StackExchange/dnscontrol/v3/providers"
_ "github.com/StackExchange/dnscontrol/v3/providers/_all"
+ "github.com/fbiville/markdown-table-formatter/pkg/markdown"
+ "sort"
)
func generateFeatureMatrix() error {
- allNames := map[string]bool{}
- for n := range providers.RegistrarTypes {
- allNames[n] = true
+ matrix := matrixData()
+ markdownTable, err := markdownTable(matrix)
+
+ if err != nil {
+ return err
}
- for n := range providers.DNSProviderTypes {
- allNames[n] = true
+
+ fmt.Print(markdownTable)
+
+ return nil
+}
+
+func markdownTable(matrix *FeatureMatrix) (string, error) {
+ var tableHeaders []string
+ tableHeaders = append(tableHeaders, "Provider name")
+ for _, featureName := range matrix.Features {
+ tableHeaders = append(tableHeaders, featureName)
}
- providerTypes := []string{}
- for n := range allNames {
- providerTypes = append(providerTypes, n)
+
+ var tableData [][]string
+ for _, providerName := range allProviderNames() {
+ featureMap := matrix.Providers[providerName]
+
+ var tableDataRow []string
+ tableDataRow = append(tableDataRow, "`"+providerName+"`")
+ for _, featureName := range matrix.Features {
+ tableDataRow = append(tableDataRow, featureEmoji(featureMap, featureName))
+ }
+ tableData = append(tableData, tableDataRow)
}
- sort.Strings(providerTypes)
+
+ var markdownTable, err = markdown.NewTableFormatterBuilder().
+ Build(tableHeaders...).
+ Format(tableData)
+ if err != nil {
+ return "", err
+ }
+
+ return markdownTable, nil
+}
+
+func featureEmoji(
+ featureMap FeatureMap,
+ featureName string,
+) string {
+ if featureMap[featureName] == nil {
+ return "❔"
+ }
+
+ if featureMap[featureName].HasFeature == true {
+ return "✅"
+ } else if featureMap[featureName].Unimplemented == true {
+ return "❔"
+ }
+ return "❌"
+}
+
+func matrixData() *FeatureMatrix {
+ const (
+ OfficialSupport = "Official Support"
+ ProviderDnsProvider = "DNS Provider"
+ ProviderRegistrar = "Registrar"
+ DomainModifierAlias = "ALIAS"
+ DomainModifierDnssec = "AUTODNSSEC"
+ DomainModifierCaa = "CAA"
+ DomainModifierPtr = "PTR"
+ DomainModifierNaptr = "NAPTR"
+ DomainModifierSoa = "SOA"
+ DomainModifierSrv = "SRV"
+ DomainModifierSshfp = "SSHFP"
+ DomainModifierTlsa = "TLSA"
+ DomainModifierDs = "DS"
+ DualHost = "dual host"
+ CreateDomains = "create-domains"
+ NoPurge = "NO_PURGE"
+ GetZones = "get-zones"
+ )
+
matrix := &FeatureMatrix{
Providers: map[string]FeatureMap{},
- Features: []FeatureDef{
- {"Official Support", "This means the provider is actively used at Stack Exchange, bugs are more likely to be fixed, and failing integration tests will block a release. See below for details"},
- {"DNS Provider", "Can manage and serve DNS zones"},
- {"Registrar", "The provider has registrar capabilities to set nameservers for zones"},
- {"ALIAS", "Provider supports some kind of ALIAS, ANAME or flattened CNAME record type"},
- {"AUTODNSSEC", "Provider can automatically manage DNSSEC"},
- {"CAA", "Provider can manage CAA records"},
- {"PTR", "Provider supports adding PTR records for reverse lookup zones"},
- {"NAPTR", "Provider can manage NAPTR records"},
- {"SOA", "Provider can manage SOA records"},
- {"SRV", "Driver has explicitly implemented SRV record management"},
- {"SSHFP", "Provider can manage SSHFP records"},
- {"TLSA", "Provider can manage TLSA records"},
- {"R53_ALIAS", "Provider supports Route 53 limited ALIAS"},
- {"AZURE_ALIAS", "Provider supports Azure DNS limited ALIAS"},
- {"DS", "Provider supports adding DS records"},
- {"AKAMAICDN", "Provider supports adding AKAMAICDN records"},
-
- {"dual host", "This provider is recommended for use in 'dual hosting' scenarios. Usually this means the provider allows full control over the apex NS records"},
- {"create-domains", "This means the provider can automatically create domains that do not currently exist on your account. The 'dnscontrol create-domains' command will initialize any missing domains"},
- {"no_purge", "indicates you can use NO_PURGE macro to prevent deleting records not managed by dnscontrol. A few providers that generate the entire zone from scratch have a problem implementing this."},
- {"get-zones", "indicates the dnscontrol get-zones subcommand is implemented."},
+ Features: []string{
+ OfficialSupport,
+ ProviderDnsProvider,
+ ProviderRegistrar,
+ DomainModifierAlias,
+ DomainModifierDnssec,
+ DomainModifierCaa,
+ DomainModifierPtr,
+ DomainModifierNaptr,
+ DomainModifierSoa,
+ DomainModifierSrv,
+ DomainModifierSshfp,
+ DomainModifierTlsa,
+ DomainModifierDs,
+ DualHost,
+ CreateDomains,
+ NoPurge,
+ GetZones,
},
}
- for _, p := range providerTypes {
- if p == "NONE" {
- continue
+
+ for _, providerName := range allProviderNames() {
+ featureMap := FeatureMap{}
+ providerNotes := providers.Notes[providerName]
+ if providerNotes == nil {
+ providerNotes = providers.DocumentationNotes{}
}
- fm := FeatureMap{}
- notes := providers.Notes[p]
- if notes == nil {
- notes = providers.DocumentationNotes{}
- }
- setCap := func(name string, cap providers.Capability) {
- if notes[cap] != nil {
- fm[name] = notes[cap]
+
+ setCapability := func(
+ featureName string,
+ capability providers.Capability,
+ ) {
+ if providerNotes[capability] != nil {
+ featureMap[featureName] = providerNotes[capability]
return
}
- fm.SetSimple(name, true, func() bool { return providers.ProviderHasCapability(p, cap) })
+ featureMap.SetSimple(
+ featureName,
+ true,
+ func() bool { return providers.ProviderHasCapability(providerName, capability) },
+ )
}
- setDoc := func(name string, cap providers.Capability, defaultNo bool) {
- if notes[cap] != nil {
- fm[name] = notes[cap]
+
+ setDocumentation := func(
+ featureName string,
+ capability providers.Capability,
+ defaultNo bool,
+ ) {
+ if providerNotes[capability] != nil {
+ featureMap[featureName] = providerNotes[capability]
} else if defaultNo {
- fm[name] = &providers.DocumentationNote{
+ featureMap[featureName] = &providers.DocumentationNote{
HasFeature: false,
}
}
}
- setDoc("Official Support", providers.DocOfficiallySupported, true)
- fm.SetSimple("DNS Provider", false, func() bool { return providers.DNSProviderTypes[p].Initializer != nil })
- fm.SetSimple("Registrar", false, func() bool { return providers.RegistrarTypes[p] != nil })
- setCap("AKAMAICDN", providers.CanUseAKAMAICDN)
- setCap("ALIAS", providers.CanUseAlias)
- setCap("AUTODNSSEC", providers.CanAutoDNSSEC)
- setCap("AZURE_ALIAS", providers.CanUseAzureAlias)
- setCap("CAA", providers.CanUseCAA)
- setCap("DS", providers.CanUseDS)
- setCap("NAPTR", providers.CanUseNAPTR)
- setCap("PTR", providers.CanUsePTR)
- setCap("R53_ALIAS", providers.CanUseRoute53Alias)
- setCap("SOA", providers.CanUseSOA)
- setCap("SRV", providers.CanUseSRV)
- setCap("SSHFP", providers.CanUseSSHFP)
- setCap("TLSA", providers.CanUseTLSA)
- setCap("get-zones", providers.CanGetZones)
- setDoc("create-domains", providers.DocCreateDomains, true)
- setDoc("dual host", providers.DocDualHost, false)
+
+ setDocumentation(
+ OfficialSupport,
+ providers.DocOfficiallySupported,
+ true,
+ )
+ featureMap.SetSimple(
+ ProviderDnsProvider,
+ false,
+ func() bool { return providers.DNSProviderTypes[providerName].Initializer != nil },
+ )
+ featureMap.SetSimple(
+ ProviderRegistrar,
+ false,
+ func() bool { return providers.RegistrarTypes[providerName] != nil },
+ )
+ setCapability(
+ DomainModifierAlias,
+ providers.CanUseAlias,
+ )
+ setCapability(
+ DomainModifierDnssec,
+ providers.CanAutoDNSSEC,
+ )
+ setCapability(
+ DomainModifierCaa,
+ providers.CanUseCAA,
+ )
+ setCapability(
+ DomainModifierDs,
+ providers.CanUseDS,
+ )
+ setCapability(
+ DomainModifierNaptr,
+ providers.CanUseNAPTR,
+ )
+ setCapability(
+ DomainModifierPtr,
+ providers.CanUsePTR,
+ )
+ setCapability(
+ DomainModifierSoa,
+ providers.CanUseSOA,
+ )
+ setCapability(
+ DomainModifierSrv,
+ providers.CanUseSRV,
+ )
+ setCapability(
+ DomainModifierSshfp,
+ providers.CanUseSSHFP,
+ )
+ setCapability(
+ DomainModifierTlsa,
+ providers.CanUseTLSA,
+ )
+ setCapability(
+ GetZones,
+ providers.CanGetZones,
+ )
+ setDocumentation(
+ CreateDomains,
+ providers.DocCreateDomains,
+ true,
+ )
+ setDocumentation(
+ DualHost,
+ providers.DocDualHost,
+ false,
+ )
// no purge is a freaky double negative
- cap := providers.CantUseNOPURGE
- if notes[cap] != nil {
- fm["no_purge"] = notes[cap]
+ cantUseNOPURGE := providers.CantUseNOPURGE
+ if providerNotes[cantUseNOPURGE] != nil {
+ featureMap[NoPurge] = providerNotes[cantUseNOPURGE]
} else {
- fm.SetSimple("no_purge", false, func() bool { return !providers.ProviderHasCapability(p, cap) })
+ featureMap.SetSimple(
+ NoPurge,
+ false,
+ func() bool { return !providers.ProviderHasCapability(providerName, cantUseNOPURGE) },
+ )
}
- matrix.Providers[p] = fm
+ matrix.Providers[providerName] = featureMap
}
- buf := &bytes.Buffer{}
- err := tmpl.Execute(buf, matrix)
- if err != nil {
- return err
- }
- return os.WriteFile("docs/_includes/matrix.html", buf.Bytes(), 0644)
+ return matrix
}
-// FeatureDef describes features.
-type FeatureDef struct {
- Name, Desc string
+func allProviderNames() []string {
+ const ProviderNameNone = "NONE"
+
+ allProviderNames := map[string]bool{}
+ for providerName := range providers.RegistrarTypes {
+ if providerName == ProviderNameNone {
+ continue
+ }
+ allProviderNames[providerName] = true
+ }
+ for providerName := range providers.DNSProviderTypes {
+ if providerName == ProviderNameNone {
+ continue
+ }
+ allProviderNames[providerName] = true
+ }
+
+ var allProviderNamesAsString []string
+ for providerName := range allProviderNames {
+ allProviderNamesAsString = append(allProviderNamesAsString, providerName)
+ }
+ sort.Strings(allProviderNamesAsString)
+
+ return allProviderNamesAsString
}
// FeatureMap maps provider names to compliance documentation.
type FeatureMap map[string]*providers.DocumentationNote
-// SetSimple configures a provider's setting in fm.
-func (fm FeatureMap) SetSimple(name string, unknownsAllowed bool, f func() bool) {
+// SetSimple configures a provider's setting in featureMap.
+func (featureMap FeatureMap) SetSimple(
+ name string,
+ unknownsAllowed bool,
+ f func() bool,
+) {
if f() {
- fm[name] = &providers.DocumentationNote{HasFeature: true}
+ featureMap[name] = &providers.DocumentationNote{HasFeature: true}
} else if !unknownsAllowed {
- fm[name] = &providers.DocumentationNote{HasFeature: false}
+ featureMap[name] = &providers.DocumentationNote{HasFeature: false}
}
}
// FeatureMatrix describes features and which providers support it.
type FeatureMatrix struct {
- Features []FeatureDef
+ Features []string
Providers map[string]FeatureMap
}
-
-var tmpl = template.Must(template.New("").Funcs(template.FuncMap{
- "safe": func(s string) template.HTML { return template.HTML(s) },
-}).Parse(`{% comment %}
- Matrix generated by build/generate/featureMatrix.go. DO NOT HAND EDIT!
-{% endcomment %}{{$providers := .Providers}}
-
-
-
-
-
-
-
- {{range .Features}}{{$name := .Name}}
- {{range $key,$val := $providers}}
- {{end -}}
-
-
- {{end -}}
-
-{{$name}}
- {{range $pname, $features := $providers}}{{$f := index $features $name}}{{if $f -}}
-
- {{if $f.Link}}{{end}}{{if $f.Link}}{{end}}
-
- {{- else}}{{end}}
- {{end -}}
-
{{include.text}}
- {{f.name}}( {% for p in f.parameters %}{% unless forloop.first == true %}, {%endunless%}{{p}}{%endfor%} )
- {% if f.return %} -> {{f.return}}{%endif%}
-