2023-07-20 02:49:34 +00:00
|
|
|
//
|
|
|
|
// DISCLAIMER
|
|
|
|
//
|
2024-01-04 12:51:28 +00:00
|
|
|
// Copyright 2023-2024 ArangoDB GmbH, Cologne, Germany
|
2023-07-20 02:49:34 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
|
|
//
|
|
|
|
|
|
|
|
package internal
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
2023-11-07 18:50:23 +00:00
|
|
|
"path/filepath"
|
2023-07-20 02:49:34 +00:00
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2023-11-07 18:50:23 +00:00
|
|
|
"github.com/coreos/go-semver/semver"
|
2023-07-20 02:49:34 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2024-05-08 10:20:14 +00:00
|
|
|
analyticsApi "github.com/arangodb/kube-arangodb/pkg/apis/analytics/v1alpha1"
|
2024-01-09 18:21:13 +00:00
|
|
|
appsApi "github.com/arangodb/kube-arangodb/pkg/apis/apps/v1"
|
2023-10-20 10:25:30 +00:00
|
|
|
backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
|
|
|
|
deploymentApi "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
2024-05-06 12:32:52 +00:00
|
|
|
mlApiv1alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/ml/v1alpha1"
|
|
|
|
mlApi "github.com/arangodb/kube-arangodb/pkg/apis/ml/v1beta1"
|
2023-10-23 07:19:59 +00:00
|
|
|
replicationApi "github.com/arangodb/kube-arangodb/pkg/apis/replication/v1"
|
2024-05-06 12:32:52 +00:00
|
|
|
schedulerApiv1alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1"
|
|
|
|
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
2023-10-23 07:19:59 +00:00
|
|
|
storageApi "github.com/arangodb/kube-arangodb/pkg/apis/storage/v1alpha"
|
2023-07-20 02:49:34 +00:00
|
|
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
|
|
|
)
|
|
|
|
|
2024-01-08 11:28:31 +00:00
|
|
|
const (
|
|
|
|
// title of docs/api/README.md page
|
|
|
|
apiIndexPageTitle = "CRD reference"
|
|
|
|
)
|
|
|
|
|
2023-11-07 18:50:23 +00:00
|
|
|
func (d DocDefinitions) RenderMarkdown(t *testing.T, repositoryPath string) []byte {
|
2023-07-20 02:49:34 +00:00
|
|
|
out := bytes.NewBuffer(nil)
|
|
|
|
|
2023-11-09 09:23:56 +00:00
|
|
|
for i, el := range d {
|
|
|
|
if i != 0 {
|
|
|
|
write(t, out, "***\n\n")
|
|
|
|
}
|
2023-07-20 02:49:34 +00:00
|
|
|
|
2023-11-09 09:23:56 +00:00
|
|
|
write(t, out, "### %s\n\n", el.Path)
|
|
|
|
write(t, out, "Type: `%s` <sup>[\\[ref\\]](%s/%s#L%d)</sup>\n\n", el.Type, repositoryPath, el.File, el.Line)
|
2023-07-20 02:49:34 +00:00
|
|
|
|
2024-02-29 15:04:06 +00:00
|
|
|
if d := el.Deprecated; len(d) > 0 {
|
|
|
|
write(t, out, "> [!WARNING]\n")
|
|
|
|
write(t, out, "> ***DEPRECATED***\n")
|
|
|
|
write(t, out, "> \n")
|
|
|
|
for _, line := range d {
|
|
|
|
write(t, out, "> **%s**\n", line)
|
|
|
|
}
|
|
|
|
write(t, out, "\n")
|
|
|
|
}
|
|
|
|
|
2023-10-19 09:41:10 +00:00
|
|
|
if d := el.Important; d != nil {
|
2024-02-29 15:04:06 +00:00
|
|
|
write(t, out, "> [!IMPORTANT]\n")
|
|
|
|
write(t, out, "> **%s**\n\n", *d)
|
2023-10-19 09:41:10 +00:00
|
|
|
}
|
|
|
|
|
2023-07-20 02:49:34 +00:00
|
|
|
if len(el.Docs) > 0 {
|
|
|
|
for _, doc := range el.Docs {
|
2023-07-24 14:53:50 +00:00
|
|
|
write(t, out, "%s\n", doc)
|
2023-07-20 02:49:34 +00:00
|
|
|
}
|
2023-07-24 14:53:50 +00:00
|
|
|
write(t, out, "\n")
|
2023-07-20 02:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(el.Links) > 0 {
|
|
|
|
write(t, out, "Links:\n")
|
|
|
|
|
|
|
|
for _, link := range el.Links {
|
|
|
|
z := strings.Split(link, "|")
|
|
|
|
if len(z) == 1 {
|
|
|
|
write(t, out, "* [Documentation](%s)\n", z[0])
|
|
|
|
} else if len(z) == 2 {
|
|
|
|
write(t, out, "* [%s](%s)\n", z[0], z[1])
|
|
|
|
} else {
|
|
|
|
require.Fail(t, "Invalid link format")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
write(t, out, "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(el.Example) > 0 {
|
|
|
|
write(t, out, "Example:\n")
|
|
|
|
write(t, out, "```yaml\n")
|
|
|
|
for _, example := range el.Example {
|
|
|
|
write(t, out, "%s\n", example)
|
|
|
|
}
|
|
|
|
write(t, out, "```\n\n")
|
|
|
|
}
|
|
|
|
|
2023-07-24 14:53:50 +00:00
|
|
|
if len(el.Enum) > 0 {
|
|
|
|
write(t, out, "Possible Values: \n")
|
|
|
|
for id, enum := range el.Enum {
|
|
|
|
z := strings.Split(enum, "|")
|
|
|
|
|
2024-01-04 12:51:28 +00:00
|
|
|
snip := fmt.Sprintf("`\"%s\"`", z[0])
|
2023-07-24 14:53:50 +00:00
|
|
|
if id == 0 {
|
2024-01-04 12:51:28 +00:00
|
|
|
snip = fmt.Sprintf("%s (default)", snip)
|
2023-07-24 14:53:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(z) == 1 {
|
2024-01-04 12:51:28 +00:00
|
|
|
write(t, out, "* %s\n", snip)
|
2023-07-24 14:53:50 +00:00
|
|
|
} else if len(z) == 2 {
|
2024-01-04 12:51:28 +00:00
|
|
|
write(t, out, "* %s - %s\n", snip, z[1])
|
2023-07-24 14:53:50 +00:00
|
|
|
} else {
|
|
|
|
require.Fail(t, "Invalid enum format")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write(t, out, "\n")
|
|
|
|
} else {
|
|
|
|
if d := el.Default; d != nil {
|
2023-11-07 18:50:23 +00:00
|
|
|
write(t, out, "Default Value: `%s`\n\n", *d)
|
2023-07-24 14:53:50 +00:00
|
|
|
}
|
2023-07-20 02:49:34 +00:00
|
|
|
}
|
|
|
|
|
2023-07-24 16:52:51 +00:00
|
|
|
if d := el.Immutable; d != nil {
|
|
|
|
write(t, out, "This field is **immutable**: %s\n\n", *d)
|
|
|
|
}
|
2023-07-20 02:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return out.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_GenerateAPIDocs(t *testing.T) {
|
|
|
|
root := os.Getenv("ROOT")
|
|
|
|
require.NotEmpty(t, root)
|
|
|
|
|
2023-11-29 13:53:44 +00:00
|
|
|
fset := token.NewFileSet()
|
|
|
|
|
2024-02-23 08:21:00 +00:00
|
|
|
type inputPackageTypes map[string]map[string]any
|
|
|
|
|
|
|
|
type inputPackage struct {
|
|
|
|
Types inputPackageTypes
|
|
|
|
|
|
|
|
Shared []string
|
|
|
|
}
|
|
|
|
|
|
|
|
type inputPackages map[string]map[string]inputPackage
|
2023-11-28 07:05:53 +00:00
|
|
|
|
2023-10-25 09:25:38 +00:00
|
|
|
// package path -> result doc file name -> name of the top-level field to be described -> field instance for reflection
|
2024-02-23 08:21:00 +00:00
|
|
|
input := inputPackages{
|
|
|
|
"deployment": map[string]inputPackage{
|
|
|
|
"v1": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"ArangoDeployment.V1": {
|
|
|
|
"Spec": deploymentApi.ArangoDeployment{}.Spec,
|
|
|
|
},
|
|
|
|
"ArangoMember.V1": {
|
|
|
|
"Spec": deploymentApi.ArangoMember{}.Spec,
|
|
|
|
},
|
|
|
|
},
|
2023-10-20 10:25:30 +00:00
|
|
|
},
|
2023-07-20 02:49:34 +00:00
|
|
|
},
|
2024-02-23 08:21:00 +00:00
|
|
|
"apps": map[string]inputPackage{
|
|
|
|
"v1": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"ArangoJob.V1": {
|
|
|
|
"Spec": appsApi.ArangoJob{}.Spec,
|
|
|
|
},
|
|
|
|
},
|
2024-01-09 18:21:13 +00:00
|
|
|
},
|
|
|
|
},
|
2024-02-23 08:21:00 +00:00
|
|
|
"backup": map[string]inputPackage{
|
|
|
|
"v1": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"ArangoBackup.V1": {
|
|
|
|
"Spec": backupApi.ArangoBackup{}.Spec,
|
|
|
|
"Status": backupApi.ArangoBackup{}.Status,
|
|
|
|
},
|
|
|
|
"ArangoBackupPolicy.V1": {
|
|
|
|
"Spec": backupApi.ArangoBackupPolicy{}.Spec,
|
|
|
|
"Status": backupApi.ArangoBackupPolicy{}.Status,
|
|
|
|
},
|
|
|
|
},
|
2023-11-08 15:36:20 +00:00
|
|
|
},
|
|
|
|
},
|
2024-02-23 08:21:00 +00:00
|
|
|
"ml": map[string]inputPackage{
|
|
|
|
"v1alpha1": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"ArangoMLExtension.V1Alpha1": {
|
2024-05-06 12:32:52 +00:00
|
|
|
"Spec": mlApiv1alpha1.ArangoMLExtension{}.Spec,
|
|
|
|
"Status": mlApiv1alpha1.ArangoMLExtension{}.Status,
|
2024-02-23 08:21:00 +00:00
|
|
|
},
|
|
|
|
"ArangoMLStorage.V1Alpha1": {
|
2024-05-06 12:32:52 +00:00
|
|
|
"Spec": mlApiv1alpha1.ArangoMLStorage{}.Spec,
|
|
|
|
"Status": mlApiv1alpha1.ArangoMLStorage{}.Status,
|
2024-02-23 08:21:00 +00:00
|
|
|
},
|
|
|
|
"ArangoMLCronJob.V1Alpha1": {
|
2024-05-06 12:32:52 +00:00
|
|
|
"Spec": mlApiv1alpha1.ArangoMLCronJob{}.Spec,
|
|
|
|
"Status": mlApiv1alpha1.ArangoMLCronJob{}.Status,
|
2024-02-23 08:21:00 +00:00
|
|
|
},
|
|
|
|
"ArangoMLBatchJob.V1Alpha1": {
|
2024-05-06 12:32:52 +00:00
|
|
|
"Spec": mlApiv1alpha1.ArangoMLBatchJob{}.Spec,
|
|
|
|
"Status": mlApiv1alpha1.ArangoMLBatchJob{}.Status,
|
2024-02-23 08:21:00 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Shared: []string{
|
|
|
|
"shared/v1",
|
|
|
|
"scheduler/v1alpha1",
|
|
|
|
"scheduler/v1alpha1/container",
|
|
|
|
"scheduler/v1alpha1/container/resources",
|
|
|
|
"scheduler/v1alpha1/pod",
|
|
|
|
"scheduler/v1alpha1/pod/resources",
|
|
|
|
},
|
2023-10-20 10:25:30 +00:00
|
|
|
},
|
2024-05-06 12:32:52 +00:00
|
|
|
"v1beta1": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"ArangoMLExtension.V1Beta1": {
|
|
|
|
"Spec": mlApi.ArangoMLExtension{}.Spec,
|
|
|
|
"Status": mlApi.ArangoMLExtension{}.Status,
|
|
|
|
},
|
|
|
|
"ArangoMLStorage.V1Beta1": {
|
|
|
|
"Spec": mlApi.ArangoMLStorage{}.Spec,
|
|
|
|
"Status": mlApi.ArangoMLStorage{}.Status,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Shared: []string{
|
|
|
|
"shared/v1",
|
|
|
|
"scheduler/v1beta1",
|
|
|
|
"scheduler/v1beta1/container",
|
|
|
|
"scheduler/v1beta1/container/resources",
|
|
|
|
"scheduler/v1beta1/pod",
|
|
|
|
"scheduler/v1beta1/pod/resources",
|
|
|
|
},
|
|
|
|
},
|
2023-09-15 11:46:38 +00:00
|
|
|
},
|
2024-05-08 10:20:14 +00:00
|
|
|
"analytics": map[string]inputPackage{
|
|
|
|
"v1alpha1": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"GraphAnalyticsEngine.V1Alpha1": {
|
|
|
|
"Spec": analyticsApi.GraphAnalyticsEngine{}.Spec,
|
|
|
|
"Status": analyticsApi.GraphAnalyticsEngine{}.Status,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Shared: []string{
|
|
|
|
"shared/v1",
|
2024-05-10 09:04:58 +00:00
|
|
|
"scheduler/v1beta1",
|
|
|
|
"scheduler/v1beta1/container",
|
|
|
|
"scheduler/v1beta1/container/resources",
|
|
|
|
"scheduler/v1beta1/pod",
|
|
|
|
"scheduler/v1beta1/pod/resources",
|
2024-05-08 10:20:14 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2024-02-23 08:21:00 +00:00
|
|
|
"replication": map[string]inputPackage{
|
|
|
|
"v1": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"ArangoDeploymentReplication.V1": {
|
|
|
|
"Spec": replicationApi.ArangoDeploymentReplication{}.Spec,
|
|
|
|
},
|
|
|
|
},
|
2023-10-23 07:19:59 +00:00
|
|
|
},
|
|
|
|
},
|
2024-03-18 10:19:27 +00:00
|
|
|
"scheduler": map[string]inputPackage{
|
|
|
|
"v1alpha1": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"ArangoProfile.V1Alpha1": {
|
2024-05-06 12:32:52 +00:00
|
|
|
"Spec": schedulerApiv1alpha1.ArangoProfile{}.Spec,
|
2024-03-18 10:19:27 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Shared: []string{
|
|
|
|
"shared/v1",
|
|
|
|
"scheduler/v1alpha1/container",
|
|
|
|
"scheduler/v1alpha1/container/resources",
|
|
|
|
"scheduler/v1alpha1/pod",
|
|
|
|
"scheduler/v1alpha1/pod/resources",
|
|
|
|
},
|
|
|
|
},
|
2024-05-06 12:32:52 +00:00
|
|
|
"v1beta1": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"ArangoProfile.V1Beta1": {
|
|
|
|
"Spec": schedulerApi.ArangoProfile{}.Spec,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Shared: []string{
|
|
|
|
"shared/v1",
|
|
|
|
"scheduler/v1beta1/container",
|
|
|
|
"scheduler/v1beta1/container/resources",
|
|
|
|
"scheduler/v1beta1/pod",
|
|
|
|
"scheduler/v1beta1/pod/resources",
|
|
|
|
},
|
|
|
|
},
|
2024-03-18 10:19:27 +00:00
|
|
|
},
|
2024-02-23 08:21:00 +00:00
|
|
|
"storage": map[string]inputPackage{
|
|
|
|
"v1alpha": {
|
|
|
|
Types: inputPackageTypes{
|
|
|
|
"ArangoLocalStorage.V1Alpha": {
|
|
|
|
"Spec": storageApi.ArangoLocalStorage{}.Spec,
|
|
|
|
},
|
|
|
|
},
|
2023-10-23 07:19:59 +00:00
|
|
|
},
|
|
|
|
},
|
2023-10-19 07:14:14 +00:00
|
|
|
}
|
|
|
|
|
2024-02-23 08:21:00 +00:00
|
|
|
for name, versions := range input {
|
|
|
|
for version, docs := range versions {
|
|
|
|
fields := parseSourceFiles(t, root, fset, path.Join(root, "pkg/apis", name, version))
|
2023-11-28 07:05:53 +00:00
|
|
|
|
2024-02-23 08:21:00 +00:00
|
|
|
for _, p := range docs.Shared {
|
|
|
|
sharedFields := parseSourceFiles(t, root, fset, path.Join(root, "pkg/apis", p))
|
2023-11-28 07:05:53 +00:00
|
|
|
|
2024-02-23 08:21:00 +00:00
|
|
|
for n, f := range sharedFields {
|
|
|
|
require.NotContains(t, fields, n)
|
|
|
|
fields[n] = f
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
generateDocs(t, docs.Types, fields, fset)
|
|
|
|
}
|
2023-10-20 10:25:30 +00:00
|
|
|
}
|
2023-07-20 02:49:34 +00:00
|
|
|
}
|
|
|
|
|
2023-11-07 18:50:23 +00:00
|
|
|
func prepareGitHubTreePath(t *testing.T, root string) string {
|
|
|
|
vStr, err := os.ReadFile(filepath.Join(root, "VERSION"))
|
|
|
|
require.NoError(t, err, "failed to read VERSION file")
|
|
|
|
opVersion, err := semver.NewVersion(string(vStr))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
ref := fmt.Sprintf("%d.%d.%d", opVersion.Major, opVersion.Minor, opVersion.Patch)
|
|
|
|
return fmt.Sprintf("https://github.com/arangodb/kube-arangodb/blob/%s", ref)
|
|
|
|
}
|
|
|
|
|
2023-10-25 09:25:38 +00:00
|
|
|
func generateDocs(t *testing.T, objects map[string]map[string]interface{}, fields map[string]*ast.Field, fs *token.FileSet) map[string]string {
|
2023-07-20 02:49:34 +00:00
|
|
|
root := os.Getenv("ROOT")
|
|
|
|
require.NotEmpty(t, root)
|
|
|
|
|
2023-10-19 07:14:14 +00:00
|
|
|
outPaths := make(map[string]string)
|
2023-07-20 02:49:34 +00:00
|
|
|
|
2023-11-07 18:50:23 +00:00
|
|
|
repositoryPath := prepareGitHubTreePath(t, root)
|
|
|
|
|
2023-10-25 09:25:38 +00:00
|
|
|
for objectName, sections := range objects {
|
|
|
|
t.Run(objectName, func(t *testing.T) {
|
2023-07-20 02:49:34 +00:00
|
|
|
renderSections := map[string][]byte{}
|
2023-10-25 09:25:38 +00:00
|
|
|
for section, fieldInstance := range sections {
|
2023-07-20 02:49:34 +00:00
|
|
|
t.Run(section, func(t *testing.T) {
|
|
|
|
|
2023-10-25 09:25:38 +00:00
|
|
|
sectionParsed := iterateOverObject(t, fields, strings.ToLower(section), reflect.TypeOf(fieldInstance), "")
|
2023-07-20 02:49:34 +00:00
|
|
|
|
2023-11-13 09:55:15 +00:00
|
|
|
defs := make(DocDefinitions, 0, len(sectionParsed))
|
|
|
|
for k, f := range sectionParsed {
|
|
|
|
defs = append(defs, parseDocDefinition(t, root, k.path, k.typ, f, fs))
|
|
|
|
}
|
2023-10-30 18:14:33 +00:00
|
|
|
defs.Sort()
|
2023-07-20 02:49:34 +00:00
|
|
|
|
2023-11-07 18:50:23 +00:00
|
|
|
renderSections[section] = defs.RenderMarkdown(t, repositoryPath)
|
2023-07-20 02:49:34 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-10-25 09:25:38 +00:00
|
|
|
fileName := fmt.Sprintf("%s.md", objectName)
|
|
|
|
outPaths[objectName] = fileName
|
|
|
|
outPath := path.Join(root, "docs/api", fmt.Sprintf("%s.md", objectName))
|
2023-10-19 07:14:14 +00:00
|
|
|
out, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
2023-07-20 02:49:34 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
require.NoError(t, out.Close())
|
|
|
|
}()
|
|
|
|
|
2024-01-08 11:28:31 +00:00
|
|
|
objName := strings.ReplaceAll(objectName, ".", " ")
|
|
|
|
writeFrontMatter(t, out, map[string]string{
|
|
|
|
"layout": "page",
|
|
|
|
"title": objName,
|
|
|
|
"parent": apiIndexPageTitle,
|
|
|
|
})
|
|
|
|
write(t, out, "# API Reference for %s\n\n", objName)
|
2023-07-20 02:49:34 +00:00
|
|
|
|
2023-10-20 10:25:30 +00:00
|
|
|
util.IterateSorted(renderSections, func(name string, section []byte) {
|
2023-07-20 02:49:34 +00:00
|
|
|
write(t, out, "## %s\n\n", name)
|
|
|
|
|
|
|
|
_, err = out.Write(section)
|
|
|
|
require.NoError(t, err)
|
2023-10-20 10:25:30 +00:00
|
|
|
})
|
2023-07-20 02:49:34 +00:00
|
|
|
})
|
|
|
|
}
|
2023-10-19 07:14:14 +00:00
|
|
|
return outPaths
|
|
|
|
}
|
|
|
|
|
2024-01-08 11:28:31 +00:00
|
|
|
func write(t *testing.T, out io.Writer, format string, args ...interface{}) {
|
|
|
|
_, err := out.Write([]byte(fmt.Sprintf(format, args...)))
|
2023-10-19 07:14:14 +00:00
|
|
|
require.NoError(t, err)
|
2024-01-08 11:28:31 +00:00
|
|
|
}
|
2023-10-19 07:14:14 +00:00
|
|
|
|
2024-01-08 11:28:31 +00:00
|
|
|
func writeFrontMatter(t *testing.T, out io.Writer, keyVals map[string]string) {
|
|
|
|
fm := ""
|
|
|
|
util.IterateSorted(keyVals, func(key, val string) {
|
|
|
|
fm += fmt.Sprintf("%s: %s\n", key, val)
|
2023-10-20 10:25:30 +00:00
|
|
|
})
|
2023-07-20 02:49:34 +00:00
|
|
|
|
2024-01-08 11:28:31 +00:00
|
|
|
if fm != "" {
|
|
|
|
fm = "---\n" + fm + "---\n\n"
|
|
|
|
}
|
|
|
|
|
|
|
|
write(t, out, fm)
|
2023-07-20 02:49:34 +00:00
|
|
|
}
|