thomasfinstad / terraform-provider-vyos-rolling

Terraform provider for VyOS with a focus on automatic resource generation
6 stars 0 forks source link

change replacement to appending #207

Closed github-actions[bot] closed 3 months ago

github-actions[bot] commented 7 months ago

milestone:6

this will need to be changed to a list

of indexes where we wish to act, then loop over

that list backwards after it is populated

as we do not with to append to the slice while

itterating over it in a forwards fashion

https://github.com/thomasfinstad/terraform-provider-vyos/blob/c3bd4b16ebe7a43fe5699e92a09c689c41d3aa65/tools/generate-changelog/main.go#L304


package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"
    "path/filepath"
    "slices"
    "strings"

    "github.com/gdexlab/go-render/render"
    jd "github.com/josephburnett/jd/lib"
    "github.com/qri-io/deepdiff"
    "github.com/wI2L/jsondiff"
)

func main() {
    var err error
    //args := os.Args[1:]
    // args := []string{
    //  "../../data/provider-schema/2024.03.21.json",
    //  "../../data/provider-schema/2024.03.22.json",
    // }

    dir := "../../data/provider-schema"
    dirEntry, err := os.ReadDir(dir)
    die(err)

    var files []string
    for _, e := range dirEntry {
        i, err := e.Info()
        die(err)

        files = append(files, fmt.Sprintf("%s/%s", dir, i.Name()))
    }

    for i := range len(files) - 1 {
        fmt.Println("\n====================================")

        // from := args[0]
        from := files[i]
        dateFrom := strings.TrimSuffix(filepath.Base(from), ".json")

        // to := args[1]
        to := files[i+1]
        dateTo := strings.TrimSuffix(filepath.Base(to), ".json")

        fmt.Println(dateFrom, "->", dateTo+"\n")

        //----------------
        // pre := `package main
        // import (jd "github.com/josephburnett/jd/lib"
        // "github.com/wI2L/jsondiff")
        // var _ = `

        // jsonDiffFile := "json-diff-" + dateFrom + "-" + dateTo + ".go"
        // err = os.WriteFile(jsonDiffFile, []byte(pre+tryJsondiff(from, to)), 0655)
        // die(err)

        //--------------------
        // pre := "# Changelog\n\n"
        // jdFile := "jd-" + dateFrom + "-" + dateTo + ".md"
        // os.WriteFile(jdFile, []byte(pre+tryJd(from, to)), 0644)
        // die(err)

        //--------------------
        pre := fmt.Sprintf("## Version %s\n", dateTo)
        jdFile := "tmp/deepdiff-" + dateFrom + "-" + dateTo + ".md"
        os.WriteFile(jdFile, []byte(pre+tryDeepDiff(from, to)), 0644)
        die(err)

        //--------------------
        // jdFile := "tmp/deepdiff-" + dateFrom + "-" + dateTo + ".json"
        // os.WriteFile(jdFile, []byte(tryDeepDiff(from, to)), 0644)
        // die(err)

    }

}

func tryJsondiff(fileA, fileB string) string {
    previousSchemaByte, err := os.ReadFile(fileA)
    die(err)

    newSchemaByte, err := os.ReadFile(fileB)
    die(err)

    var previousSchema map[string]any
    err = json.Unmarshal(previousSchemaByte, &previousSchema)
    die(err)

    var newSchema map[string]any
    err = json.Unmarshal(newSchemaByte, &newSchema)
    die(err)

    patch, err := jsondiff.Compare(previousSchema, newSchema)
    die(err)

    return render.Render(patch)
}

func tryJd(fileA, fileB string) string {
    aNode, err := jd.ReadJsonFile(fileA)
    die(err)
    bNode, err := jd.ReadJsonFile(fileB)
    die(err)

    diff := aNode.Diff(bNode)

    changes := make(map[string]string)

    for _, e := range diff {
        action := "Change that occured: deleted/added/changed"
        typeName := "Type: Resource/Data source"
        blockName := "Resource/Data source name"

        if len(e.NewValues) == 0 && len(e.OldValues) != 0 {
            action = "## Removed\n"
        } else if len(e.NewValues) != 0 && len(e.OldValues) == 0 {
            action = "## Added\n"
        } else if len(e.NewValues) != 0 && len(e.OldValues) != 0 {
            action = "## Changed\n"
        } else {
            die(fmt.Errorf("can not determine action: %#v", e))
        }

        if len(e.Path) < 3 {
            die(fmt.Errorf("too short to determine type: %#v", e.Render()))
        } else if fmt.Sprint(e.Path[2]) == "resource_schemas" {
            typeName = "Resource"
        } else if fmt.Sprint(e.Path[2]) == "data_source_schemas" {
            typeName = "Data source"
        } else if fmt.Sprint(e.Path[2]) == "provider" {
            typeName = "Provider config"

        } else {
            die(fmt.Errorf("can not determine type:\n%s", e.Render()))
        }

        blockName = fmt.Sprint(e.Path[3])
        switch len(e.Path) {
        case 1:
            fmt.Printf("%#v", e)
        case 4:
            changes[action] += fmt.Sprintf("%s: %s \n\n", typeName, blockName)
        case 6:
            changes[action] += fmt.Sprintf("%s: %s Attribute (6)\n*  %s \n\n", typeName, blockName, e.Path[5])
        case 7:
            changes[action] += fmt.Sprintf("%s: %s Attribute (7)\n*  %s \n\n", typeName, blockName, e.Path[6])
        default:
            if len(e.Path) > 6 {
                // "provider_schemas", "local/providers/vyos", "resource_schemas", "vyos_qos_policy_limiter_class", "block", "attributes", "mtu"

                // "provider_schemas", "local/providers/vyos", "resource_schemas", "vyos_qos_policy_limiter", "block", "attributes", "default", "nested_type", "attributes", "mtu"

                switch fmt.Sprint(e.Path[5]) {
                case "attributes":
                    changes[action] += fmt.Sprintf("%s: %s (attributes)\n* %s.%s \n\n", typeName, blockName, e.Path[6], e.Path[7])
                case "block_types":
                    changes[action] += fmt.Sprintf("%s: %s Attribute (block_types)\n* %s \n\n", typeName, blockName, e.Path[5])
                default:
                    die(fmt.Errorf("we might need to go deeper: %#v", e))
                }
            } else {
                die(fmt.Errorf("can not determine what was changed: %#v", e))
            }
        }
    }

    chglog := ""

    for k, v := range changes {
        chglog += k + v
    }

    return chglog
}

func tryDeepDiff(fileA, fileB string) string {
    previousSchemaByte, err := os.ReadFile(fileA)
    die(err)

    newSchemaByte, err := os.ReadFile(fileB)
    die(err)

    var previousSchema map[string]any
    err = json.Unmarshal(previousSchemaByte, &previousSchema)
    die(err)

    var newSchema map[string]any
    err = json.Unmarshal(newSchemaByte, &newSchema)
    die(err)

    dd := deepdiff.New(func(cfg *deepdiff.Config) { cfg.CalcChanges = true })

    rootDeltas, err := dd.Diff(context.TODO(), previousSchema, newSchema)
    die(err)

    var removeNoopDeltas func(d deepdiff.Deltas) deepdiff.Deltas
    removeNoopDeltas = func(d deepdiff.Deltas) deepdiff.Deltas {
        for i := len(d) - 1; i >= 0; i-- {
            d[i].Deltas = removeNoopDeltas(d[i].Deltas)
            if d[i].Deltas.Len() == 0 && d[i].Type == deepdiff.DTContext {
                d = slices.Delete(d, i, i+1)
            }
        }
        return d
    }
    rootDeltas = removeNoopDeltas(rootDeltas)

    var changes = make(map[string]map[string][]string)
    var organizeChanges func(d deepdiff.Deltas, path, kind string)
    organizeChanges = func(d deepdiff.Deltas, path, kind string) {
        for _, dd := range d {

            if dd.Path.String() == "provider_schemas" {
                organizeChanges(dd.Deltas[0].Deltas, path, kind)
                continue
            }

            subpath := path

            switch dd.Path.String() {
            case "resource_schemas":
                kind = "Resources"
            case "data_source_schemas":
                kind = "Data Sources"
            case "provider":
                kind = "Provider Config"
            case "block", "attributes", "nested_type":
                // Noop
            default:
                if path == "" {
                    subpath = dd.Path.String()
                } else {
                    subpath = subpath + "." + dd.Path.String()
                }
            }

            if changes[kind] == nil {
                changes[kind] = make(map[string][]string)
            }

            if dd.Type == deepdiff.DTContext {
                organizeChanges(dd.Deltas, subpath, kind)
                continue
            }

            switch dd.Type {
            case deepdiff.DTDelete:
                changes[kind]["Remove"] = append(changes[kind]["Remove"], subpath)
            case deepdiff.DTInsert:
                changes[kind]["Add"] = append(changes[kind]["Add"], subpath)
            case deepdiff.DTUpdate:
                changes[kind]["Change"] = append(changes[kind]["Change"], subpath)
            default:
                fmt.Println(dd)
                panic("unknown deepdiff type")
            }
        }
    }

    organizeChanges(rootDeltas, "", "")

    r := ""

    for kind, v := range changes {
        r += fmt.Sprintf("### %s\n", kind)
        for op, paths := range v {
            r += fmt.Sprintf("#### %s\n", op)
            for _, path := range paths {
                r += fmt.Sprintf("* %s\n\n", path)
            }
        }
    }

    return r
}

// AddLineBreaks is a very naive way to add line breaks
// to a .go file string. It came about as a solution when
// regex replace were too slow to be feasable
func AddLineBreaks(r string) string {
    rr := []rune(r)
    var pa rune
    var insideQuote bool
    var quote rune
    for pos, char := range rr {
        if insideQuote {
            if char == quote {
                if char == '`' {
                    insideQuote = false
                } else if pa != '\\' {
                    insideQuote = false
                }
            }
        } else {
            if char == '"' || char == '\'' || char == '`' {
                quote = char
                insideQuote = true
            } else {
                if char == ',' {
                    // TODO change replacement to appending
                    //  milestone:6
                    //  this will need to be changed to a list
                    //  of indexes where we wish to act, then loop over
                    //  that list backwards after it is populated
                    //  as we do not with to append to the slice while
                    //  itterating over it in a forwards fashion
                    rr[pos+1] = '\n'
                }
            }
        }
        pa = char
    }
    return string(rr)
}
github-actions[bot] commented 3 months ago

Closed in a36ab456517346b48a7491c52b9509cdc13d49ad