blob: cc6425cb39c92ee7e822f9a943a31398f3f4e89b [file] [log] [blame]
Jack Neus1c9298a2019-07-08 09:40:41 -06001// Copyright 2019 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.
Jack Neus3fb42782019-06-27 10:13:12 -06004package main
5
6import (
Julio Hurtado36804fc2020-11-03 19:12:31 +00007 "context"
Jack Neus3fb42782019-06-27 10:13:12 -06008 "github.com/maruel/subcommands"
Sean Abraham842ea6c2020-06-25 16:52:12 -06009 "go.chromium.org/chromiumos/infra/go/internal/branch"
Jack Neusb0aeb842019-08-20 10:18:20 -060010 mv "go.chromium.org/chromiumos/infra/go/internal/chromeos_version"
Julio Hurtado36804fc2020-11-03 19:12:31 +000011 "go.chromium.org/chromiumos/infra/go/internal/gerrit"
Jack Neusde4ed492019-08-20 15:26:33 -060012 "go.chromium.org/chromiumos/infra/go/internal/git"
Jack Neus07088fa2019-08-20 11:58:08 -060013 "go.chromium.org/chromiumos/infra/go/internal/repo"
Sean Abraham842ea6c2020-06-25 16:52:12 -060014 "go.chromium.org/luci/auth"
Jack Neus1c9298a2019-07-08 09:40:41 -060015 "go.chromium.org/luci/common/errors"
Julio Hurtado516392f2020-11-12 20:19:35 +000016 "io/ioutil"
Mike Frysingerd6ff88f2020-12-22 22:58:22 -050017 "os"
Julio Hurtado516392f2020-11-12 20:19:35 +000018 "strings"
Jack Neus1c9298a2019-07-08 09:40:41 -060019)
20
Julio Hurtado36804fc2020-11-03 19:12:31 +000021const (
22 branchCreatorGroup = "mdb/chromeos-branch-creators"
23)
24
25func getCmdCreateBranch(opts auth.Options) *subcommands.Command {
Sean Abraham7e0369b2020-06-23 14:10:08 -060026 return &subcommands.Command{
Julio Hurtado36804fc2020-11-03 19:12:31 +000027 UsageLine: "create <options>",
28 ShortDesc: "Create a branch.",
29 LongDesc: "Create a branch using the newer Gerrit API-based branching approach.",
Sean Abraham7e0369b2020-06-23 14:10:08 -060030 CommandRun: func() subcommands.CommandRun {
Julio Hurtado36804fc2020-11-03 19:12:31 +000031 c := &createBranch{}
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -060032 c.InitFlags(opts)
Sean Abraham7e0369b2020-06-23 14:10:08 -060033 // Arguments for determining branch name.
Julio Hurtado9972f732020-11-04 19:34:28 +000034 c.Flags.StringVar(&c.file, "file", "",
35 "File path to manifest file. Can be either absolute or relative to branch_util binary.")
Sean Abraham7e0369b2020-06-23 14:10:08 -060036 c.Flags.StringVar(&c.descriptor, "descriptor", "",
37 "Optional descriptor for this branch. Typically, this is a build "+
38 "target or a device, depending on the nature of the branch. Used "+
39 "to generate the branch name. Cannot be used with --custom.")
Julio Hurtado36804fc2020-11-03 19:12:31 +000040 c.Flags.StringVar(&c.buildSpecManifest, "buildspec-manifest", "",
41 "Path to manifest within manifest-versions repo, relative to "+
Mike Frysingerfde46512020-12-22 18:31:51 -050042 "https://chrome-internal.googlesource.com/chromeos/manifest-versions/+/HEAD/buildspecs/ "+
Julio Hurtado36804fc2020-11-03 19:12:31 +000043 "e.g. 85/13277.0.0.xml")
Sean Abraham7e0369b2020-06-23 14:10:08 -060044 c.Flags.BoolVar(&c.release, "release", false,
45 "The new branch is a release branch. "+
46 "Named as 'release-<descriptor>-R<Milestone>-<Major Version>.B'.")
47 c.Flags.BoolVar(&c.factory, "factory", false,
48 "The new branch is a factory branch. "+
49 "Named as 'factory-<Descriptor>-<Major Version>.B'.")
50 c.Flags.BoolVar(&c.firmware, "firmware", false,
51 "The new branch is a firmware branch. "+
52 "Named as 'firmware-<Descriptor>-<Major Version>.B'.")
53 c.Flags.BoolVar(&c.stabilize, "stabilize", false,
54 "The new branch is a minibranch. "+
55 "Named as 'stabilize-<Descriptor>-<Major Version>.B'.")
56 c.Flags.StringVar(&c.custom, "custom", "",
57 "Use a custom branch type with an explicit name. "+
58 "WARNING: custom names are dangerous. This tool greps branch "+
59 "names to determine which versions have already been branched. "+
60 "Version validation is not possible when the naming convention "+
61 "is broken. Use this at your own risk.")
Julio Hurtado36804fc2020-11-03 19:12:31 +000062 c.Flags.Float64Var(&c.gerritWriteQps, "gerrit-write-qps", 1.0,
63 "Maximum QPS to use for Gerrit API write operations.")
Sean Abraham7e0369b2020-06-23 14:10:08 -060064 return c
65 },
66 }
Jack Neus3fb42782019-06-27 10:13:12 -060067}
68
Julio Hurtado36804fc2020-11-03 19:12:31 +000069type createBranch struct {
Jack Neus3fb42782019-06-27 10:13:12 -060070 CommonFlags
Julio Hurtado36804fc2020-11-03 19:12:31 +000071 yes bool
72 descriptor string
Julio Hurtado36804fc2020-11-03 19:12:31 +000073 buildSpecManifest string
74 release bool
75 factory bool
76 firmware bool
77 stabilize bool
78 custom string
79 gerritWriteQps float64
80 file string
Jack Neus3fb42782019-06-27 10:13:12 -060081}
82
Julio Hurtado36804fc2020-11-03 19:12:31 +000083func (c *createBranch) validate(args []string) (bool, string) {
Julio Hurtado9972f732020-11-04 19:34:28 +000084 if c.buildSpecManifest == "" && c.file == "" {
85 return false, "must set --buildspec-manifest or --file"
86 }
87 if c.buildSpecManifest != "" && c.file != "" {
88 return false, "--buildspec-manifest and --file cannot be used together"
Jack Neus3fb42782019-06-27 10:13:12 -060089 }
Sean Abraham842ea6c2020-06-25 16:52:12 -060090 _, ok := branch.BranchType(c.release, c.factory, c.firmware, c.stabilize, c.custom)
Jack Neus3fb42782019-06-27 10:13:12 -060091 if !ok {
Jack Neus1c9298a2019-07-08 09:40:41 -060092 return false, "must select exactly one branch type " +
Jack Neus3fb42782019-06-27 10:13:12 -060093 "(--release, --factory, --firmware, --stabilize, --custom)."
94 }
Jack Neus4c3d5182019-06-27 10:01:16 -060095 if c.descriptor != "" && c.custom != "" {
96 return false, "--descriptor cannot be used with --custom."
97 }
Jack Neus3fb42782019-06-27 10:13:12 -060098 return true, ""
99}
100
Jack Neus4c3d5182019-06-27 10:01:16 -0600101// Getters so that functions using the branchCommand interface
102// can access CommonFlags in the underlying struct.
Julio Hurtado36804fc2020-11-03 19:12:31 +0000103func (c *createBranch) getRoot() string {
Jack Neus4c3d5182019-06-27 10:01:16 -0600104 return c.Root
105}
106
Julio Hurtado36804fc2020-11-03 19:12:31 +0000107func (c *createBranch) getManifestUrl() string {
Jack Neus4c3d5182019-06-27 10:01:16 -0600108 return c.ManifestUrl
109}
110
Julio Hurtado36804fc2020-11-03 19:12:31 +0000111func (c *createBranch) Run(a subcommands.Application, args []string,
Jack Neus3fb42782019-06-27 10:13:12 -0600112 env subcommands.Env) int {
Jack Neus4c3d5182019-06-27 10:01:16 -0600113 // Common setup (argument validation, repo init, etc.)
Julio Hurtado36804fc2020-11-03 19:12:31 +0000114
Jack Neus4c3d5182019-06-27 10:01:16 -0600115 ret := Run(c, a, args, env)
116 if ret != 0 {
117 return ret
Jack Neus3fb42782019-06-27 10:13:12 -0600118 }
Julio Hurtado36804fc2020-11-03 19:12:31 +0000119 ctx := context.Background()
120 authOpts, err := c.authFlags.Options()
121 if err != nil {
122 branch.LogErr(errors.Annotate(err, "failed to configure auth").Err().Error())
123 return 1
124 }
125
126 authedClient, err := auth.NewAuthenticator(ctx, auth.SilentLogin, authOpts).Client()
127
128 if err != nil {
Mike Frysingerd6ff88f2020-12-22 22:58:22 -0500129 branch.LogErr(errors.Annotate(err, "Please run `%s auth-login` and sign in with your @google.com account", os.Args[0]).Err().Error())
Julio Hurtado36804fc2020-11-03 19:12:31 +0000130 return 1
131 }
132
133 if c.Push {
134 inGroup, err := branch.CheckSelfGroupMembership(authedClient, "https://chromium-review.googlesource.com", branchCreatorGroup)
135 if err != nil {
136 branch.LogErr(errors.Annotate(err, "failed to confirm that the running user is in %v", branchCreatorGroup).Err().Error())
137 return 1
138 }
139 if !inGroup {
140 branch.LogErr("you appear not to be in %v, and so you won't be able to create a branch.\n"+
141 "See http://go/cros-branch#access for instructions for gaining access.", branchCreatorGroup)
142 return 1
143 }
144 }
145
Jack Neus07088fa2019-08-20 11:58:08 -0600146 if c.file != "" {
147 // Branch from file.
Julio Hurtado36804fc2020-11-03 19:12:31 +0000148 file, err := repo.LoadManifestFromFileWithIncludes(c.file)
149 if err != nil {
150 branch.LogErr(errors.Annotate(err, "Error: Failed to load manifest from file ").Err().Error())
151 return 1
152 }
153 branch.LogErr("Got manifest from filepath %v", c.file)
154 branch.WorkingManifest = *file
155 } else {
156 file, err := gerrit.DownloadFileFromGitiles(authedClient, ctx, "chrome-internal.googlesource.com",
Julio Hurtado516392f2020-11-12 20:19:35 +0000157 "chromeos/manifest-versions", "main", "buildspecs/"+c.buildSpecManifest)
158
159 // Temporary fix for while repos are being renamed due to COIL Initiative
160 if strings.Contains(err.Error(), "NotFound") || strings.Contains(err.Error(), "not found") {
161 file, err = gerrit.DownloadFileFromGitiles(authedClient, ctx, "chrome-internal.googlesource.com",
162 "chromeos/manifest-versions", "master", "buildspecs/"+c.buildSpecManifest)
163 }
Julio Hurtado36804fc2020-11-03 19:12:31 +0000164 if err != nil {
165 branch.LogErr(errors.Annotate(err, "failed to fetch buildspec %v", c.buildSpecManifest).Err().Error())
166 return 1
167 }
168 branch.LogErr("Got %v from Gitiles", c.buildSpecManifest)
169 wm, err := ioutil.TempFile("", "working-manifest.xml")
170 if err != nil {
171 branch.LogErr("%s\n", err.Error())
172 return 1
173 }
174 _, err = wm.WriteString(file)
175 if err != nil {
176 branch.LogErr("%s\n", err.Error())
177 return 1
178 }
179 branch.WorkingManifest, err = repo.LoadManifestFromFile(wm.Name())
Jack Neus07088fa2019-08-20 11:58:08 -0600180 if err != nil {
181 err = errors.Annotate(err, "failed to load manifests").Err()
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600182 branch.LogErr("%s\n", err.Error())
Jack Neus07088fa2019-08-20 11:58:08 -0600183 return 1
184 }
Julio Hurtado36804fc2020-11-03 19:12:31 +0000185 branch.LogErr("Fetched working manifest.\n")
Jack Neus34842752019-08-14 11:31:23 -0600186 }
Jack Neus3fb42782019-06-27 10:13:12 -0600187
Jack Neus8c232b42019-08-21 19:35:11 -0600188 // Use manifest-internal as a sentinel repository to get the appropriate branch name.
189 // We know that manifest-internal is a single-checkout so its revision should be
190 // master or the name of the Chrome OS branch.
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600191 manifestInternal, err := branch.WorkingManifest.GetUniqueProject("chromeos/manifest-internal")
Jack Neus8c232b42019-08-21 19:35:11 -0600192 if err != nil {
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600193 branch.LogErr(errors.Annotate(err, "Could not get chromeos/manifest-internal project.").Err().Error())
Julio Hurtado36804fc2020-11-03 19:12:31 +0000194 return 1
Jack Neus8c232b42019-08-21 19:35:11 -0600195 }
196 sourceRevision := manifestInternal.Revision
Sean Abraham6f071d42019-08-28 09:06:34 -0600197 sourceUpstream := git.StripRefs(manifestInternal.Upstream)
Julio Hurtado8770a662021-01-13 22:31:29 +0000198
199 // This string replacement is needed since chromiumos-overlay's master branch
200 // has been renamed to main. This replacement swaps the name so the correct
201 // upstream is used. crbug.com/1163216 for reference.
202 if sourceUpstream == "master" {
203 sourceUpstream = "main"
204 }
205
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600206 branch.LogErr("Using sourceRevision %s for manifestInternal", sourceRevision)
207 branch.LogErr("Using sourceUpstream %s for manifestInternal", sourceUpstream)
Jack Neus8c232b42019-08-21 19:35:11 -0600208
Jack Neus1c9298a2019-07-08 09:40:41 -0600209 // Validate the version.
Julio Hurtado36804fc2020-11-03 19:12:31 +0000210 // Double check that the checkout has a zero patch number. Otherwise we cannot branch from it.
Sean Abraham7ed78ba2020-06-24 17:35:42 -0600211 versionProject, err := branch.WorkingManifest.GetProjectByPath(branch.VersionFileProjectPath)
Jack Neus8c232b42019-08-21 19:35:11 -0600212 if err != nil {
Sean Abraham7ed78ba2020-06-24 17:35:42 -0600213 err = errors.Annotate(err, "could not get project %s from manifest", branch.VersionFileProjectPath).Err()
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600214 branch.LogErr("%s\n", err)
Julio Hurtado36804fc2020-11-03 19:12:31 +0000215 return 1
Jack Neus57e8a5a2019-08-12 17:43:10 -0600216 }
217
Julio Hurtado36804fc2020-11-03 19:12:31 +0000218 // Fetch chromeos_version.sh from the source branch
219 versionFile, err := gerrit.DownloadFileFromGitiles(authedClient, ctx,
220 "chromium.googlesource.com", versionProject.Name, versionProject.Revision, mv.VersionFileProjectPath)
221
222 if err != nil {
223 branch.LogErr(errors.Annotate(err, "failed to fetch versionFile").Err().Error())
224 return 1
225 }
226
227 vinfo, err := mv.ParseVersionInfo([]byte(versionFile))
Jack Neus6c72a8a2019-07-15 09:52:21 -0600228 if err != nil {
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600229 branch.LogErr(errors.Annotate(err, "error reading version").Err().Error())
Julio Hurtado36804fc2020-11-03 19:12:31 +0000230 return 1
Jack Neus6c72a8a2019-07-15 09:52:21 -0600231 }
Julio Hurtado36804fc2020-11-03 19:12:31 +0000232
Jack Neus1c9298a2019-07-08 09:40:41 -0600233 if vinfo.PatchNumber != 0 {
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600234 branch.LogErr("Cannot branch version with nonzero patch number (version %s).",
Jack Neus1c9298a2019-07-08 09:40:41 -0600235 vinfo.VersionString())
Julio Hurtado36804fc2020-11-03 19:12:31 +0000236 return 1
Jack Neus1c9298a2019-07-08 09:40:41 -0600237 }
Julio Hurtado36804fc2020-11-03 19:12:31 +0000238 branch.LogErr("Version found: %s.\n", vinfo.VersionString())
239
240 branch.LogErr("Have manifest = %v", manifestInternal)
Jack Neus1c9298a2019-07-08 09:40:41 -0600241
Julio Hurtado7eb30d92020-10-07 21:51:06 +0000242 branchType := ""
243
244 switch {
245 case c.release:
246 branchType = "release"
247 case c.factory:
248 branchType = "factory"
249 case c.firmware:
250 branchType = "firmware"
251 case c.stabilize:
252 branchType = "stabilize"
253 default:
254 branchType = "custom"
255
256 }
257
258 if err = branch.CheckIfAlreadyBranched(vinfo, manifestInternal, c.Force, branchType); err != nil {
Sean Abraham842ea6c2020-06-25 16:52:12 -0600259 branch.LogErr("%v", err)
260 return 1
Jack Neus1c9298a2019-07-08 09:40:41 -0600261 }
262
Sean Abraham842ea6c2020-06-25 16:52:12 -0600263 branchName := branch.NewBranchName(vinfo, c.custom, c.descriptor, c.release, c.factory, c.firmware, c.stabilize)
Sean Abraham7ed78ba2020-06-24 17:35:42 -0600264 componentToBump, err := branch.WhichVersionShouldBump(vinfo)
Jack Neus6c72a8a2019-07-15 09:52:21 -0600265 if err != nil {
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600266 branch.LogErr(err.Error())
Julio Hurtado36804fc2020-11-03 19:12:31 +0000267 return 1
Jack Neus6c72a8a2019-07-15 09:52:21 -0600268 }
269
Jack Neuscc825422019-07-11 10:32:54 -0600270 // Generate git branch names.
Sean Abraham7ed78ba2020-06-24 17:35:42 -0600271 branches := branch.ProjectBranches(branchName, git.StripRefs(sourceRevision))
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600272 branch.LogOut("Creating branch: %s\n", branchName)
Jack Neusde4ed492019-08-20 15:26:33 -0600273
Julio Hurtado36804fc2020-11-03 19:12:31 +0000274 projectBranches, err := branch.GerritProjectBranches(branches)
275 if err != nil {
276 branch.LogErr(err.Error())
277 return 1
Jack Neuscc825422019-07-11 10:32:54 -0600278 }
279
Jack Neusdb35be32019-08-23 13:13:10 -0600280 // Repair manifest repositories.
Sean Abraham7ed78ba2020-06-24 17:35:42 -0600281 if err = branch.RepairManifestRepositories(branches, !c.Push, c.Force); err != nil {
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600282 branch.LogErr(err.Error())
Julio Hurtado36804fc2020-11-03 19:12:31 +0000283 return 1
Jack Neuscc825422019-07-11 10:32:54 -0600284 }
Julio Hurtado36804fc2020-11-03 19:12:31 +0000285
286 // Create git branches for new branch. Exclude the ManifestProjects, which we just updated.
287 if err = branch.CreateRemoteBranchesApi(authedClient, branch.GetNonManifestBranches(projectBranches), !c.Push, c.gerritWriteQps); err != nil {
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600288 branch.LogErr(err.Error())
Julio Hurtado36804fc2020-11-03 19:12:31 +0000289 return 1
Jack Neusd2a15a42019-08-13 15:38:45 -0600290 }
Jack Neuscc825422019-07-11 10:32:54 -0600291
Jack Neus6c72a8a2019-07-15 09:52:21 -0600292 // Bump version.
Sean Abraham7ed78ba2020-06-24 17:35:42 -0600293 if err = branch.BumpForCreate(componentToBump, c.release, c.Push, branchName, sourceUpstream); err != nil {
Sean Abrahamc2c5a6a2020-06-23 17:54:41 -0600294 branch.LogErr(err.Error())
Julio Hurtado36804fc2020-11-03 19:12:31 +0000295 return 1
Jack Neus6c72a8a2019-07-15 09:52:21 -0600296 }
Jack Neus8c232b42019-08-21 19:35:11 -0600297
Julio Hurtado36804fc2020-11-03 19:12:31 +0000298 if !c.Push {
299 branch.LogErr("Dry run (no --push): completed successfully")
300 }
Jack Neus3fb42782019-06-27 10:13:12 -0600301 return 0
302}