| // Copyright 2023 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package proto |
| |
| import ( |
| "context" |
| "errors" |
| "fmt" |
| "io/fs" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "strings" |
| |
| "go.fuchsia.dev/infra/cmd/roller-configurator/jiri" |
| ) |
| |
| func (s *GitSubmodule) url(ctx context.Context, repoRoot string) (string, error) { |
| gitmodules := filepath.Join(repoRoot, ".gitmodules") |
| if _, err := os.Stat(gitmodules); err != nil { |
| return "", fmt.Errorf("no .gitmodules file in repository root") |
| } |
| |
| // TODO(olivernewman): Run `git config --list` once to get all submodule |
| // info and cache the result instead of running a subprocess for every |
| // submodule. |
| cmd := exec.CommandContext(ctx, "git", "config", "--list", "--file", gitmodules) |
| var stdout, stderr strings.Builder |
| cmd.Stdout = &stdout |
| cmd.Stderr = &stderr |
| if err := cmd.Run(); err != nil { |
| var suffix string |
| if stderr.Len() > 0 { |
| suffix = fmt.Sprintf(": %s", stderr.String()) |
| } |
| return "", fmt.Errorf("%w%s", err, suffix) |
| } |
| for _, line := range strings.Split(strings.TrimSpace(stdout.String()), "\n") { |
| key, val, ok := strings.Cut(line, "=") |
| if !ok { |
| return "", fmt.Errorf("invalid `git config --list --file .gitmodules` line: %q", line) |
| } |
| // Assumes the submodule's path is the same as its name. |
| if key == fmt.Sprintf("submodule.%s.url", s.GetPath()) { |
| return val, nil |
| } |
| } |
| return "", fmt.Errorf("no such submodule %q listed in .gitmodules", s.GetPath()) |
| } |
| |
| func (s *GitSubmodule) Validate(ctx context.Context, repoRoot string) error { |
| _, err := s.url(ctx, repoRoot) |
| return err |
| } |
| |
| func (c *CIPDEnsureFile) Validate(ctx context.Context, repoRoot string) error { |
| if _, err := os.Stat(filepath.Join(repoRoot, c.Path)); err != nil { |
| return fmt.Errorf("no such file: %s", c.Path) |
| } |
| return nil |
| } |
| |
| func (p *JiriProject) Validate(ctx context.Context, repoRoot string) error { |
| _, err := p.manifestEntry(repoRoot) |
| return err |
| } |
| |
| func (p *JiriProject) manifestEntry(repoRoot string) (jiri.Project, error) { |
| manifest, err := jiri.LoadManifest(filepath.Join(repoRoot, p.Manifest)) |
| if err != nil { |
| if errors.Is(err, fs.ErrNotExist) { |
| err = fmt.Errorf("no such file: %s", p.Manifest) |
| } |
| return jiri.Project{}, err |
| } |
| for _, proj := range manifest.Projects { |
| if proj.Name == p.Project { |
| return proj, nil |
| } |
| } |
| return jiri.Project{}, fmt.Errorf("no project %q in manifest %q", p.Project, p.Manifest) |
| } |
| |
| func (p *JiriPackages) Validate(ctx context.Context, repoRoot string) error { |
| _, err := p.manifestEntries(repoRoot) |
| return err |
| } |
| |
| func (p *JiriPackages) manifestEntries(repoRoot string) ([]jiri.Package, error) { |
| var entries []jiri.Package |
| for _, m := range p.Manifests { |
| manifest, err := jiri.LoadManifest(filepath.Join(repoRoot, m.Path)) |
| if err != nil { |
| if errors.Is(err, fs.ErrNotExist) { |
| err = fmt.Errorf("no such file: %s", m.Path) |
| } |
| return nil, err |
| } |
| for _, pkg := range m.Packages { |
| var found bool |
| for _, entry := range manifest.Packages { |
| if entry.Name == pkg { |
| entries = append(entries, entry) |
| found = true |
| } |
| } |
| if !found { |
| return nil, fmt.Errorf("no package %q in manifest %q", pkg, m.Path) |
| } |
| } |
| } |
| return entries, nil |
| } |