aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandNewBuild.java
blob: d6cba28449be7c1da3c67b1fa0c7308bb123cff1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//
// Copyright (C) 2019 The Qt Company
//

package com.googlesource.gerrit.plugins.qtcodereview;

import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.InternalUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.NoSuchRefException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gerrit.sshd.SshCommand;
import com.google.gerrit.sshd.CommandMetaData;

import com.google.gwtorm.server.OrmException;

import com.google.inject.Inject;
import com.google.inject.Provider;

import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.kohsuke.args4j.Option;

import java.io.IOException;
import java.util.List;
import java.util.Map.Entry;

@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.")
class QtCommandNewBuild extends SshCommand {

    @Inject
    private PermissionBackend permissionBackend;

    @Inject
    private GitRepositoryManager gitManager;

    @Inject
    private Provider<ReviewDb> dbProvider;

    @Inject
    private BatchUpdate.Factory updateFactory;

    @Inject
    private QtUtil qtUtil;

    @Inject
    private QtChangeUpdateOp.Factory qtUpdateFactory;

    @Option(name = "--project", aliases = {"-p"},
        required = true, usage = "project name")
    private String project;

    @Option(name = "--staging-branch", aliases = {"-s"},
        required = true, usage = "branch name, e.g. refs/staging/master or just master")
    private String stagingBranch;

    @Option(name = "--build-id", aliases = {"-i"},
        required = true, usage = "build id, e.g. refs/builds/my_build or just my_build")
    private String build;

    private static final FluentLogger logger = FluentLogger.forEnclosingClass();

    private Repository git;


    @Override
    protected void run() throws UnloggedFailure {

        logger.atInfo().log("qtcodereview: staging-new-build -p %s -s %s -i %s", project, stagingBranch, build);

        try {
            Project.NameKey projectKey = new Project.NameKey(project);
            git = gitManager.openRepository(projectKey);

            Branch.NameKey buildBranchKey = QtUtil.getNameKeyLong(project, QtUtil.R_BUILDS, build);
            Branch.NameKey stagingBranchKey = QtUtil.getNameKeyLong(project, QtUtil.R_STAGING, stagingBranch);
            Branch.NameKey destBranchShortKey =  QtUtil.getNameKeyShort(project, QtUtil.R_STAGING, stagingBranch);
            Branch.NameKey destinationKey = QtUtil.getNameKeyLong(project, QtUtil.R_HEADS, stagingBranch);

            // Check required permissions
            permissionBackend.user(user).project(projectKey).ref(destinationKey.get()).check(RefPermission.UPDATE);
            permissionBackend.user(user).project(projectKey).ref(buildBranchKey.get()).check(RefPermission.CREATE);

            if (QtUtil.branchExists(git, buildBranchKey) == true) {
                logger.atSevere().log("qtcodereview: staging-new-build Target build %s already exists", buildBranchKey);
                throw die("Target build already exists!");
            }

            if (QtUtil.branchExists(git, stagingBranchKey) == false) {
                logger.atSevere().log("qtcodereview: staging-new-build staging ref %s not found", stagingBranchKey);
                throw die("Staging ref not found!");
            }

            // Create build reference.
            Result result = qtUtil.createBuildRef(git, user.asIdentifiedUser(),
                                                  projectKey, stagingBranchKey, buildBranchKey);
            String message = String.format("Added to build %s for %s", build, destinationKey);

            if (result != Result.NEW && result != Result.FAST_FORWARD) {
                logger.atSevere().log("qtcodereview: staging-new-build failed to create new build ref %s result %s",
                                      buildBranchKey, result);
                throw new UnloggedFailure(1, "fatal: failed to create new build ref: " + result);
            } else {
                // list the changes in staging branch but missing from the destination branch
                List<Entry<ChangeData, RevCommit>> openChanges = qtUtil.listChangesNotMerged(git, buildBranchKey, destBranchShortKey);

                // Make sure that there are changes in the staging branch.
                if (openChanges.isEmpty()) {
                    logger.atSevere().log("qtcodereview: staging-new-build No changes in staging branch %s.", stagingBranchKey);
                    throw die("No changes in staging branch. Not creating a build reference");
                }

                QtChangeUpdateOp op = qtUpdateFactory.create(Change.Status.INTEGRATING, message, null, null, null);
                try (BatchUpdate u =  updateFactory.create(dbProvider.get(), projectKey, user, TimeUtil.nowTs())) {
                    for (Entry<ChangeData, RevCommit> item: openChanges) {
                        Change change = item.getKey().change();
                        logger.atInfo().log("qtcodereview: staging-new-build     inserted change %s (%s) into build %s for %s",
                                            change, item.getValue().toString(), build, destinationKey);
                        u.addOp(change.getId(), op);
                    }
                    u.execute();
                }
            }

            logger.atInfo().log("qtcodereview: staging-new-build build %s for %s created", build, destBranchShortKey);

        } catch (AuthException e) {
            logger.atSevere().log("qtcodereview: staging-new-build Authentication failed to access repository: %s", e);
            throw die("Authentication failed to access repository");
        } catch (PermissionBackendException e) {
            logger.atSevere().log("qtcodereview: staging-new-build Not enough permissions to access repository %s", e);
            throw die("Not enough permissions to access repository");
        } catch (RepositoryNotFoundException e) {
            throw die("project not found");
        } catch (IOException e) {
            logger.atSevere().log("qtcodereview: staging-new-build Failed to access repository %s", e);
            throw die("Failed to access repository");
        } catch (OrmException e) {
            logger.atSevere().log("qtcodereview: staging-new-build Failed to access database %s", e);
            throw die("Failed to access database");
        } catch (QtUtil.BranchNotFoundException e) {
            logger.atSevere().log("qtcodereview: staging-new-build Failed to access build or staging ref %s", e);
            throw die("Failed to access build or staging ref");
        } catch (NoSuchRefException e) {
            logger.atSevere().log("qtcodereview: staging-new-build Invalid branch name %s", e);
            throw die("Invalid branch name");
        } catch (UpdateException | RestApiException e) {
            logger.atSevere().log("qtcodereview: staging-new-build failed to update change status %s", e);
            throw die("Failed to update change status");
        } finally {
            if (git != null) {
                git.close();
            }
        }
    }

}