go.fuchsia.dev/infra@v0.0.0-20240507153436-9b593402251b/cmd/roller-configurator/proto/validators.go (about) 1 // Copyright 2023 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package proto 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "io/fs" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "strings" 16 17 "go.fuchsia.dev/infra/cmd/roller-configurator/jiri" 18 ) 19 20 func (s *GitSubmodule) url(ctx context.Context, repoRoot string) (string, error) { 21 gitmodules := filepath.Join(repoRoot, ".gitmodules") 22 if _, err := os.Stat(gitmodules); err != nil { 23 return "", fmt.Errorf("no .gitmodules file in repository root") 24 } 25 26 // TODO(olivernewman): Run `git config --list` once to get all submodule 27 // info and cache the result instead of running a subprocess for every 28 // submodule. 29 cmd := exec.CommandContext(ctx, "git", "config", "--list", "--file", gitmodules) 30 var stdout, stderr strings.Builder 31 cmd.Stdout = &stdout 32 cmd.Stderr = &stderr 33 if err := cmd.Run(); err != nil { 34 var suffix string 35 if stderr.Len() > 0 { 36 suffix = fmt.Sprintf(": %s", stderr.String()) 37 } 38 return "", fmt.Errorf("%w%s", err, suffix) 39 } 40 for _, line := range strings.Split(strings.TrimSpace(stdout.String()), "\n") { 41 key, val, ok := strings.Cut(line, "=") 42 if !ok { 43 return "", fmt.Errorf("invalid `git config --list --file .gitmodules` line: %q", line) 44 } 45 // Assumes the submodule's path is the same as its name. 46 if key == fmt.Sprintf("submodule.%s.url", s.GetPath()) { 47 return val, nil 48 } 49 } 50 return "", fmt.Errorf("no such submodule %q listed in .gitmodules", s.GetPath()) 51 } 52 53 func (s *GitSubmodule) Validate(ctx context.Context, repoRoot string) error { 54 _, err := s.url(ctx, repoRoot) 55 return err 56 } 57 58 func (c *CIPDEnsureFile) Validate(ctx context.Context, repoRoot string) error { 59 if _, err := os.Stat(filepath.Join(repoRoot, c.Path)); err != nil { 60 return fmt.Errorf("no such file: %s", c.Path) 61 } 62 return nil 63 } 64 65 func (p *JiriProject) Validate(ctx context.Context, repoRoot string) error { 66 _, err := p.manifestEntry(repoRoot) 67 return err 68 } 69 70 func (p *JiriProject) manifestEntry(repoRoot string) (jiri.Project, error) { 71 manifest, err := jiri.LoadManifest(filepath.Join(repoRoot, p.Manifest)) 72 if err != nil { 73 if errors.Is(err, fs.ErrNotExist) { 74 err = fmt.Errorf("no such file: %s", p.Manifest) 75 } 76 return jiri.Project{}, err 77 } 78 for _, proj := range manifest.Projects { 79 if proj.Name == p.Project { 80 return proj, nil 81 } 82 } 83 return jiri.Project{}, fmt.Errorf("no project %q in manifest %q", p.Project, p.Manifest) 84 } 85 86 func (p *JiriPackages) Validate(ctx context.Context, repoRoot string) error { 87 _, err := p.manifestEntries(repoRoot) 88 return err 89 } 90 91 func (p *JiriPackages) manifestEntries(repoRoot string) ([]jiri.Package, error) { 92 var entries []jiri.Package 93 for _, m := range p.Manifests { 94 manifest, err := jiri.LoadManifest(filepath.Join(repoRoot, m.Path)) 95 if err != nil { 96 if errors.Is(err, fs.ErrNotExist) { 97 err = fmt.Errorf("no such file: %s", m.Path) 98 } 99 return nil, err 100 } 101 for _, pkg := range m.Packages { 102 var found bool 103 for _, entry := range manifest.Packages { 104 if entry.Name == pkg { 105 entries = append(entries, entry) 106 found = true 107 } 108 } 109 if !found { 110 return nil, fmt.Errorf("no package %q in manifest %q", pkg, m.Path) 111 } 112 } 113 } 114 return entries, nil 115 }