// Copyright (c) 2020 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License.AGPL.txt in the project root for license information. package main import ( "fmt" "io" "io/fs" "os" "path/filepath" "sort" "strings" "golang.org/x/xerrors" "gopkg.in/yaml.v3" ) type MD struct { HelmComponent string `yaml:"helm-component"` } func main() { produceManifest(os.Stdout, os.DirFS(".")) } func produceManifest(out io.Writer, dir fs.FS) error { mds, err := fs.Glob(dir, "**/metadata.yaml") if err != nil { return err } versions := make(map[string]string) for _, md := range mds { b, err := fs.ReadFile(dir, md) if err != nil { return err } var mdobj MD err = yaml.Unmarshal(b, &mdobj) if err != nil { return xerrors.Errorf("cannot unmarshal %s: %w", md, err) } if mdobj.HelmComponent == "" { continue } imgf, err := fs.ReadFile(dir, filepath.Join(filepath.Dir(md), "imgnames.txt")) if err != nil { return xerrors.Errorf("cannot read image names for %s: %w", md, err) } imgs := strings.Split(strings.TrimSpace(string(imgf)), "\n") img := imgs[len(imgs)-1] segs := strings.Split(img, ":") if len(segs) != 2 { return xerrors.Errorf("invalid image format: %s", img) } version := segs[1] versions[mdobj.HelmComponent] = version } // We need to deduplicate keys in the resulting yaml file. To this end, we first build up // a map of maps and later print that map YAML style. res := make(map[string]interface{}) comps := make(map[string]interface{}) res["components"] = comps for k, v := range versions { var ( m = comps segs = strings.Split(k+".version", ".") ) for i, seg := range segs { if i == len(segs)-1 { m[seg] = v continue } if _, ok := m[seg]; !ok { m[seg] = make(map[string]interface{}) } m = m[seg].(map[string]interface{}) } } // It's not clear how to maintain a stable order of keys using the YAML serializer. // If it were, we could just through this map at the YAML serializer and call it a day. // Right now, we have to produce the YAML ourselves. var print func(m map[string]interface{}, indent int) error print = func(m map[string]interface{}, indent int) error { keys := make([]string, 0, len(m)) for v := range m { keys = append(keys, v) } sort.Strings(keys) for _, k := range keys { v := m[k] fmt.Fprintf(out, "%s%s:", strings.Repeat(" ", indent), k) if c, ok := v.(map[string]interface{}); ok { fmt.Fprintln(out) err := print(c, indent+1) if err != nil { return err } continue } if c, ok := v.(string); ok { fmt.Fprintf(out, " %s\n", c) fmt.Fprintln(out) continue } return xerrors.Errorf("unknown value type - this should never happen") } return nil } return print(res, 0) }