mirror of
https://github.com/hashicorp/packer.git
synced 2026-04-22 14:47:19 -04:00
Modify makefile to call generate code properly, setting project root. 'make generate' now avoids deleting website code generated in the packer plugin sdk. For now it will be maintainers' responsibility to regenerate this docs code from the packer plugin sdk every release, and commit it to these folders manually. remove boot command generator code
178 lines
4 KiB
Go
178 lines
4 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/fatih/camelcase"
|
|
"github.com/fatih/structtag"
|
|
)
|
|
|
|
func main() {
|
|
args := flag.Args()
|
|
if len(args) == 0 {
|
|
// Default: process the file
|
|
args = []string{os.Getenv("GOFILE")}
|
|
}
|
|
fname := args[0]
|
|
|
|
absFilePath, err := filepath.Abs(fname)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
projectRoot := os.Getenv("PROJECT_ROOT")
|
|
var paths []string
|
|
if projectRoot == "" {
|
|
// fall back to the packer root.
|
|
paths = strings.SplitAfter(absFilePath, "packer"+string(os.PathSeparator))
|
|
projectRoot = paths[0]
|
|
} else {
|
|
paths = strings.SplitAfter(absFilePath, projectRoot+string(os.PathSeparator))
|
|
}
|
|
builderName, _ := filepath.Split(paths[1])
|
|
builderName = strings.Trim(builderName, string(os.PathSeparator))
|
|
|
|
b, err := ioutil.ReadFile(fname)
|
|
if err != nil {
|
|
fmt.Printf("ReadFile: %+v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fset := token.NewFileSet()
|
|
f, err := parser.ParseFile(fset, fname, b, parser.ParseComments)
|
|
if err != nil {
|
|
fmt.Printf("ParseFile: %+v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
for _, decl := range f.Decls {
|
|
typeDecl, ok := decl.(*ast.GenDecl)
|
|
if !ok {
|
|
continue
|
|
}
|
|
typeSpec, ok := typeDecl.Specs[0].(*ast.TypeSpec)
|
|
if !ok {
|
|
continue
|
|
}
|
|
structDecl, ok := typeSpec.Type.(*ast.StructType)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
fields := structDecl.Fields.List
|
|
sourcePath := filepath.ToSlash(paths[1])
|
|
header := Struct{
|
|
SourcePath: sourcePath,
|
|
Name: typeSpec.Name.Name,
|
|
Filename: typeSpec.Name.Name + ".mdx",
|
|
Header: strings.TrimSpace(typeDecl.Doc.Text()),
|
|
}
|
|
required := Struct{
|
|
SourcePath: sourcePath,
|
|
Name: typeSpec.Name.Name,
|
|
Filename: typeSpec.Name.Name + "-required.mdx",
|
|
}
|
|
notRequired := Struct{
|
|
SourcePath: sourcePath,
|
|
Name: typeSpec.Name.Name,
|
|
Filename: typeSpec.Name.Name + "-not-required.mdx",
|
|
}
|
|
|
|
for _, field := range fields {
|
|
if len(field.Names) == 0 || field.Tag == nil {
|
|
continue
|
|
}
|
|
tag := field.Tag.Value[1:]
|
|
tag = tag[:len(tag)-1]
|
|
tags, err := structtag.Parse(tag)
|
|
if err != nil {
|
|
fmt.Printf("structtag.Parse(%s): err: %v", field.Tag.Value, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Leave undocumented tags out of markdown. This is useful for
|
|
// fields which exist for backwards compatability, or internal-use
|
|
// only fields
|
|
undocumented, _ := tags.Get("undocumented")
|
|
if undocumented != nil {
|
|
if undocumented.Name == "true" {
|
|
continue
|
|
}
|
|
}
|
|
mstr, err := tags.Get("mapstructure")
|
|
if err != nil {
|
|
continue
|
|
}
|
|
name := mstr.Name
|
|
|
|
if name == "" {
|
|
continue
|
|
}
|
|
|
|
var docs string
|
|
if field.Doc != nil {
|
|
docs = field.Doc.Text()
|
|
} else {
|
|
docs = strings.Join(camelcase.Split(field.Names[0].Name), " ")
|
|
}
|
|
|
|
if strings.Contains(docs, "TODO") {
|
|
continue
|
|
}
|
|
fieldType := string(b[field.Type.Pos()-1 : field.Type.End()-1])
|
|
fieldType = strings.ReplaceAll(fieldType, "*", `\*`)
|
|
switch fieldType {
|
|
case "time.Duration":
|
|
fieldType = `duration string | ex: "1h5m2s"`
|
|
case "config.Trilean":
|
|
fieldType = `boolean`
|
|
case "config.NameValues":
|
|
fieldType = `[]{name string, value string}`
|
|
case "config.KeyValues":
|
|
fieldType = `[]{key string, value string}`
|
|
}
|
|
|
|
field := Field{
|
|
Name: name,
|
|
Type: fieldType,
|
|
Docs: docs,
|
|
}
|
|
if req, err := tags.Get("required"); err == nil && req.Value() == "true" {
|
|
required.Fields = append(required.Fields, field)
|
|
} else {
|
|
notRequired.Fields = append(notRequired.Fields, field)
|
|
}
|
|
}
|
|
|
|
dir := filepath.Join(projectRoot, "website", "pages", "partials", builderName)
|
|
os.MkdirAll(dir, 0755)
|
|
|
|
for _, str := range []Struct{header, required, notRequired} {
|
|
if len(str.Fields) == 0 && len(str.Header) == 0 {
|
|
continue
|
|
}
|
|
outputPath := filepath.Join(dir, str.Filename)
|
|
|
|
outputFile, err := os.Create(outputPath)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer outputFile.Close()
|
|
|
|
err = structDocsTemplate.Execute(outputFile, str)
|
|
if err != nil {
|
|
fmt.Printf("%v", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|