blob: 0ec6c88c52cab69848414adafb6e7f61c61f941c [file] [log] [blame]
Sean Abraham47cc5852020-06-30 09:32:41 -06001// Copyright 2020 The Chromium OS 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
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -06005package branch
6
7import (
8 "fmt"
9 "go.chromium.org/chromiumos/infra/go/internal/git"
10 "go.chromium.org/chromiumos/infra/go/internal/repo"
11 "go.chromium.org/luci/common/errors"
12 "io/ioutil"
13 "log"
14 "net/url"
15 "path"
16 "path/filepath"
17 "strconv"
18)
19
Sean Abraham7ed78ba2020-06-24 17:35:42 -060020const VersionFileProjectPath = "src/third_party/chromiumos-overlay"
21
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -060022var (
23 StdoutLog *log.Logger
24 StderrLog *log.Logger
25 WorkingManifest repo.Manifest
26 RepoToolPath string
27 ManifestCheckout string
28)
29
30// CheckoutOptions describes how to check out a Git repo.
31type CheckoutOptions struct {
32 // If set, will get only this Ref.
33 // If not set, will get the full repo.
34 Ref string
35 // To be used with the git clone --depth flag.
36 Depth int
37}
38
39// LogOut logs to stdout.
40func LogOut(format string, a ...interface{}) {
41 if StdoutLog != nil {
42 StdoutLog.Printf(format, a...)
43 }
44}
45
46// LogOut logs to stderr.
47func LogErr(format string, a ...interface{}) {
48 if StderrLog != nil {
49 StderrLog.Printf(format, a...)
50 }
51}
52
Sean Abraham7ed78ba2020-06-24 17:35:42 -060053// ProjectFetchUrl returns the fetch URL for a remote Project.
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -060054func ProjectFetchUrl(projectPath string) (string, error) {
55 project, err := WorkingManifest.GetProjectByPath(projectPath)
56 if err != nil {
57 return "", err
58 }
59
60 remote := WorkingManifest.GetRemoteByName(project.RemoteName)
61 if remote == nil {
62 return "", fmt.Errorf("remote %s does not exist in working manifest", project.RemoteName)
63 }
64 projectUrl, err := url.Parse(remote.Fetch)
65 if err != nil {
66 return "", errors.Annotate(err, "failed to parse fetch location for remote %s", remote.Name).Err()
67 }
68 projectUrl.Path = path.Join(projectUrl.Path, project.Name)
69
70 return projectUrl.String(), nil
71}
72
73func getProjectCheckoutFromUrl(projectUrl string, opts *CheckoutOptions) (string, error) {
74 checkoutDir, err := ioutil.TempDir("", "cros-branch-")
75 if err != nil {
76 return "", errors.Annotate(err, "tmp dir could not be created").Err()
77 }
78
79 if err := git.Init(checkoutDir, false); err != nil {
80 return "", err
81 }
82 if err := git.AddRemote(checkoutDir, "origin", projectUrl); err != nil {
83 return "", errors.Annotate(err, "could not add %s as remote", projectUrl).Err()
84 }
85
86 cmd := []string{"fetch", "origin"}
87 if opts != nil {
88 if opts.Ref != "" {
89 cmd = append(cmd, git.StripRefs(opts.Ref))
90 }
91 if opts.Depth > 0 {
92 cmd = append(cmd, "--depth", strconv.Itoa(opts.Depth))
93 }
94 }
95 output, err := git.RunGit(checkoutDir, cmd)
96 if err != nil {
97 return "", fmt.Errorf("failed to fetch %s: %s", projectUrl, output.Stderr)
98 }
99 checkoutBranch := "master"
100 if opts != nil && opts.Ref != "" {
101 checkoutBranch = git.StripRefs(opts.Ref)
102 }
103 if err := git.Checkout(checkoutDir, checkoutBranch); err != nil {
104 return "", fmt.Errorf("failed to checkout %s", checkoutBranch)
105 }
106
107 return checkoutDir, nil
108}
109
110// GetProjectCheckout gets a local checkout of a particular project.
111func GetProjectCheckout(projectPath string, opts *CheckoutOptions) (string, error) {
112 projectUrl, err := ProjectFetchUrl(projectPath)
113
114 if err != nil {
115 return "", errors.Annotate(err, "failed to get project fetch url").Err()
116 }
117 return getProjectCheckoutFromUrl(projectUrl, opts)
118}
119
120// InitWorkingManifest initializes a local working manifest (a.k.a. buildspec)
121// from a Gerrit path.
122func InitWorkingManifest(manifestUrl, br string) error {
123 opts := &CheckoutOptions{
124 Depth: 1,
125 Ref: br,
126 }
127 var err error
128 ManifestCheckout, err = getProjectCheckoutFromUrl(manifestUrl, opts)
129 if err != nil {
130 return errors.Annotate(err, "could not checkout %s", manifestUrl).Err()
131 }
132
133 if br != "" {
134 err := git.Checkout(ManifestCheckout, br)
135 if err != nil {
136 return errors.Annotate(err, "failed to checkout br %s of %s", br, manifestUrl).Err()
137 }
138 }
139
140 manifestPath := filepath.Join(ManifestCheckout, "default.xml")
141
142 // Read in manifest from file (and resolve includes).
143 manifest, err := repo.LoadManifestFromFileWithIncludes(manifestPath)
144 if err != nil {
145 return errors.Annotate(err, "failed to load manifests").Err()
146 }
147 WorkingManifest = *manifest
148 return nil
149}