|
| 1 | +// |
| 2 | +// Copyright (C) 2019 The Qt Company |
| 3 | +// |
| 4 | + |
| 5 | +package com.googlesource.gerrit.plugins.qtcodereview; |
| 6 | + |
| 7 | +import com.google.common.flogger.FluentLogger; |
| 8 | +import com.google.gerrit.extensions.restapi.AuthException; |
| 9 | +import com.google.gerrit.extensions.restapi.RestApiException; |
| 10 | +import com.google.gerrit.reviewdb.client.Branch; |
| 11 | +import com.google.gerrit.reviewdb.client.Change; |
| 12 | +import com.google.gerrit.reviewdb.client.PatchSet; |
| 13 | +import com.google.gerrit.reviewdb.client.Project; |
| 14 | +import com.google.gerrit.reviewdb.server.ReviewDb; |
| 15 | +import com.google.gerrit.server.InternalUser; |
| 16 | +import com.google.gerrit.server.git.GitRepositoryManager; |
| 17 | +import com.google.gerrit.server.permissions.PermissionBackend; |
| 18 | +import com.google.gerrit.server.permissions.PermissionBackendException; |
| 19 | +import com.google.gerrit.server.permissions.ProjectPermission; |
| 20 | +import com.google.gerrit.server.permissions.RefPermission; |
| 21 | +import com.google.gerrit.server.project.NoSuchProjectException; |
| 22 | +import com.google.gerrit.server.project.NoSuchRefException; |
| 23 | +import com.google.gerrit.server.query.change.ChangeData; |
| 24 | +import com.google.gerrit.server.update.BatchUpdate; |
| 25 | +import com.google.gerrit.server.update.UpdateException; |
| 26 | +import com.google.gerrit.server.util.time.TimeUtil; |
| 27 | +import com.google.gerrit.sshd.SshCommand; |
| 28 | +import com.google.gerrit.sshd.CommandMetaData; |
| 29 | + |
| 30 | +import com.google.gwtorm.server.OrmException; |
| 31 | + |
| 32 | +import com.google.inject.Inject; |
| 33 | +import com.google.inject.Provider; |
| 34 | + |
| 35 | +import org.eclipse.jgit.errors.MissingObjectException; |
| 36 | +import org.eclipse.jgit.errors.RepositoryNotFoundException; |
| 37 | +import org.eclipse.jgit.lib.RefUpdate.Result; |
| 38 | +import org.eclipse.jgit.lib.Repository; |
| 39 | +import org.eclipse.jgit.revwalk.RevCommit; |
| 40 | +import org.kohsuke.args4j.Option; |
| 41 | + |
| 42 | +import java.io.IOException; |
| 43 | +import java.util.List; |
| 44 | +import java.util.Map.Entry; |
| 45 | + |
| 46 | +@CommandMetaData(name="staging-new-build", description="Create unique build branch of the current staging branch and change the gerrit status of the changes to INTEGRATING.") |
| 47 | +class QtCommandNewBuild extends SshCommand { |
| 48 | + |
| 49 | + @Inject |
| 50 | + private PermissionBackend permissionBackend; |
| 51 | + |
| 52 | + @Inject |
| 53 | + private GitRepositoryManager gitManager; |
| 54 | + |
| 55 | + @Inject |
| 56 | + private Provider<ReviewDb> dbProvider; |
| 57 | + |
| 58 | + @Inject |
| 59 | + private BatchUpdate.Factory updateFactory; |
| 60 | + |
| 61 | + @Inject |
| 62 | + private QtUtil qtUtil; |
| 63 | + |
| 64 | + @Inject |
| 65 | + private QtChangeUpdateOp.Factory qtUpdateFactory; |
| 66 | + |
| 67 | + @Option(name = "--project", aliases = {"-p"}, |
| 68 | + required = true, usage = "project name") |
| 69 | + private String project; |
| 70 | + |
| 71 | + @Option(name = "--staging-branch", aliases = {"-s"}, |
| 72 | + required = true, usage = "branch name, e.g. refs/staging/master or just master") |
| 73 | + private String stagingBranch; |
| 74 | + |
| 75 | + @Option(name = "--build-id", aliases = {"-i"}, |
| 76 | + required = true, usage = "build id, e.g. refs/builds/my_build or just my_build") |
| 77 | + private String build; |
| 78 | + |
| 79 | + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); |
| 80 | + |
| 81 | + private Repository git; |
| 82 | + |
| 83 | + |
| 84 | + @Override |
| 85 | + protected void run() throws UnloggedFailure { |
| 86 | + |
| 87 | + logger.atInfo().log("qtcodereview: staging-new-build -p %s -s %s -i %s", project, stagingBranch, build); |
| 88 | + |
| 89 | + try { |
| 90 | + Project.NameKey projectKey = new Project.NameKey(project); |
| 91 | + git = gitManager.openRepository(projectKey); |
| 92 | + |
| 93 | + Branch.NameKey buildBranchKey = QtUtil.getNameKeyLong(project, QtUtil.R_BUILDS, build); |
| 94 | + Branch.NameKey stagingBranchKey = QtUtil.getNameKeyLong(project, QtUtil.R_STAGING, stagingBranch); |
| 95 | + Branch.NameKey destBranchShortKey = QtUtil.getNameKeyShort(project, QtUtil.R_STAGING, stagingBranch); |
| 96 | + Branch.NameKey destinationKey = QtUtil.getNameKeyLong(project, QtUtil.R_HEADS, stagingBranch); |
| 97 | + |
| 98 | + // Check required permissions |
| 99 | + permissionBackend.user(user).project(projectKey).ref(destinationKey.get()).check(RefPermission.UPDATE); |
| 100 | + permissionBackend.user(user).project(projectKey).ref(buildBranchKey.get()).check(RefPermission.CREATE); |
| 101 | + |
| 102 | + if (QtUtil.branchExists(git, buildBranchKey) == true) { |
| 103 | + logger.atSevere().log("qtcodereview: staging-new-build Target build %s already exists", buildBranchKey); |
| 104 | + throw die("Target build already exists!"); |
| 105 | + } |
| 106 | + |
| 107 | + if (QtUtil.branchExists(git, stagingBranchKey) == false) { |
| 108 | + logger.atSevere().log("qtcodereview: staging-new-build staging ref %s not found", stagingBranchKey); |
| 109 | + throw die("Staging ref not found!"); |
| 110 | + } |
| 111 | + |
| 112 | + // Create build reference. |
| 113 | + Result result = qtUtil.createBuildRef(git, user.asIdentifiedUser(), |
| 114 | + projectKey, stagingBranchKey, buildBranchKey); |
| 115 | + String message = String.format("Added to build %s for %s", build, destinationKey); |
| 116 | + |
| 117 | + if (result != Result.NEW && result != Result.FAST_FORWARD) { |
| 118 | + logger.atSevere().log("qtcodereview: staging-new-build failed to create new build ref %s result %s", |
| 119 | + buildBranchKey, result); |
| 120 | + throw new UnloggedFailure(1, "fatal: failed to create new build ref: " + result); |
| 121 | + } else { |
| 122 | + // list the changes in staging branch but missing from the destination branch |
| 123 | + List<Entry<ChangeData, RevCommit>> openChanges = qtUtil.listChangesNotMerged(git, buildBranchKey, destBranchShortKey); |
| 124 | + |
| 125 | + // Make sure that there are changes in the staging branch. |
| 126 | + if (openChanges.isEmpty()) { |
| 127 | + logger.atSevere().log("qtcodereview: staging-new-build No changes in staging branch %s.", stagingBranchKey); |
| 128 | + throw die("No changes in staging branch. Not creating a build reference"); |
| 129 | + } |
| 130 | + |
| 131 | + QtChangeUpdateOp op = qtUpdateFactory.create(Change.Status.INTEGRATING, message, null, null, null); |
| 132 | + try (BatchUpdate u = updateFactory.create(dbProvider.get(), projectKey, user, TimeUtil.nowTs())) { |
| 133 | + for (Entry<ChangeData, RevCommit> item: openChanges) { |
| 134 | + Change change = item.getKey().change(); |
| 135 | + logger.atInfo().log("qtcodereview: staging-new-build inserted change %s (%s) into build %s for %s", |
| 136 | + change, item.getValue().toString(), build, destinationKey); |
| 137 | + u.addOp(change.getId(), op); |
| 138 | + } |
| 139 | + u.execute(); |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + logger.atInfo().log("qtcodereview: staging-new-build build %s for %s created", build, destBranchShortKey); |
| 144 | + |
| 145 | + } catch (AuthException e) { |
| 146 | + logger.atSevere().log("qtcodereview: staging-new-build Authentication failed to access repository: %s", e); |
| 147 | + throw die("Authentication failed to access repository"); |
| 148 | + } catch (PermissionBackendException e) { |
| 149 | + logger.atSevere().log("qtcodereview: staging-new-build Not enough permissions to access repository %s", e); |
| 150 | + throw die("Not enough permissions to access repository"); |
| 151 | + } catch (RepositoryNotFoundException e) { |
| 152 | + throw die("project not found"); |
| 153 | + } catch (IOException e) { |
| 154 | + logger.atSevere().log("qtcodereview: staging-new-build Failed to access repository %s", e); |
| 155 | + throw die("Failed to access repository"); |
| 156 | + } catch (OrmException e) { |
| 157 | + logger.atSevere().log("qtcodereview: staging-new-build Failed to access database %s", e); |
| 158 | + throw die("Failed to access database"); |
| 159 | + } catch (QtUtil.BranchNotFoundException e) { |
| 160 | + logger.atSevere().log("qtcodereview: staging-new-build Failed to access build or staging ref %s", e); |
| 161 | + throw die("Failed to access build or staging ref"); |
| 162 | + } catch (NoSuchRefException e) { |
| 163 | + logger.atSevere().log("qtcodereview: staging-new-build Invalid branch name %s", e); |
| 164 | + throw die("Invalid branch name"); |
| 165 | + } catch (UpdateException | RestApiException e) { |
| 166 | + logger.atSevere().log("qtcodereview: staging-new-build failed to update change status %s", e); |
| 167 | + throw die("Failed to update change status"); |
| 168 | + } finally { |
| 169 | + if (git != null) { |
| 170 | + git.close(); |
| 171 | + } |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | +} |
0 commit comments