aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJukka Jokiniva <jukka.jokiniva@qt.io>2019-05-22 17:26:03 +0300
committerJukka Jokiniva <jukka.jokiniva@qt.io>2019-05-23 07:07:23 +0000
commit6b6abb74af3d76491b3658c672dde2593d3e23e3 (patch)
tree33f81a872430f416df4fb4add60d15c04ca5b3d6
parentef970bc31996f781070f7079c4fd731993101bb6 (diff)
Add ssh command api for staging
Fixes: QTQAINFRA-2989 Change-Id: I204153146a5c321fcc87dbabcf8b180264b897e4 Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io>
-rw-r--r--src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandStage.java76
-rw-r--r--src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java1
-rw-r--r--src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandStageIT.java168
3 files changed, 245 insertions, 0 deletions
diff --git a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandStage.java b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandStage.java
new file mode 100644
index 0000000..5a851c4
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandStage.java
@@ -0,0 +1,76 @@
+//
+// Copyright (C) 2019 The Qt Company
+//
+
+package com.googlesource.gerrit.plugins.qtcodereview;
+
+import com.google.common.flogger.FluentLogger;
+
+import com.google.gerrit.extensions.api.changes.SubmitInput;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.restapi.change.ChangesCollection;
+import com.google.gerrit.server.restapi.change.Revisions;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.sshd.SshCommand;
+import com.google.gerrit.sshd.CommandMetaData;
+import com.google.gerrit.sshd.commands.PatchSetParser;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import java.util.HashSet;
+import java.util.Set;
+import org.kohsuke.args4j.Argument;
+
+@CommandMetaData(name="stage", description="Stage a change.")
+class QtCommandStage extends SshCommand {
+
+ @Inject private QtStage qtStage;
+ @Inject private ChangesCollection changes;
+ @Inject private PatchSetParser psParser;
+ @Inject private Revisions revisions;
+
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final Set<PatchSet> patchSets = new HashSet<>();
+
+ @Argument(
+ index = 0,
+ required = true,
+ multiValued = true,
+ metaVar = "{COMMIT | CHANGE,PATCHSET}",
+ usage = "list of commits or patch sets to stage")
+ void addPatchSetId(String token) {
+ try {
+ PatchSet ps = psParser.parsePatchSet(token, null, null);
+ patchSets.add(ps);
+ } catch (UnloggedFailure e) {
+ throw new IllegalArgumentException(e.getMessage(), e);
+ } catch (OrmException e) {
+ throw new IllegalArgumentException("database error", e);
+ }
+ }
+
+ @Override
+ protected void run() throws UnloggedFailure {
+ boolean ok = true;
+
+ for (PatchSet patchSet : patchSets) {
+ try {
+ logger.atInfo().log("qtcodereview: ssh command stage %s", patchSet.getId());
+ ChangeResource c = changes.parse(patchSet.getId().getParentKey());
+ IdString id = IdString.fromDecoded(patchSet.getRevision().get());
+ RevisionResource r = revisions.parse(c, id);
+ qtStage.apply(r, new SubmitInput());
+ } catch (Exception e) {
+ ok = false;
+ writeError("error", e.getMessage() + "\n");
+ }
+ }
+
+ if (!ok) {
+ throw die("one or more stages failed; review output above");
+ }
+ }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java
index bc97492..8d72353 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/qtcodereview/QtSshModule.java
@@ -16,5 +16,6 @@ class QtSshModule extends PluginCommandModule {
command(QtCommandNewBuild.class);
command(QtCommandListStaging.class);
command(QtCommandRebuildStaging.class);
+ command(QtCommandStage.class);
}
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandStageIT.java b/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandStageIT.java
new file mode 100644
index 0000000..8f0171f
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/qtcodereview/QtCommandStageIT.java
@@ -0,0 +1,168 @@
+// Copyright (C) 2019 The Qt Company
+
+package com.googlesource.gerrit.plugins.qtcodereview;
+
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.ChangeMessage;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Before;
+import org.junit.Test;
+
+@TestPlugin(
+ name = "gerrit-plugin-qt-workflow",
+ sysModule = "com.googlesource.gerrit.plugins.qtcodereview.QtModule",
+ sshModule = "com.googlesource.gerrit.plugins.qtcodereview.QtSshModule"
+)
+
+@UseSsh
+public class QtCommandStageIT extends QtCodeReviewIT {
+
+ @Before
+ public void SetDefaultPermissions() throws Exception {
+ grant(project, "refs/heads/master", Permission.QT_STAGE, false, REGISTERED_USERS);
+ }
+
+ @Test
+ public void single_Change_Stage() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+ approve(c.getChangeId());
+ qtSshStage(c);
+ }
+
+ @Test
+ public void multi_Change_Stage() throws Exception {
+ // Push 3 independent commits
+ RevCommit initialHead = getRemoteHead();
+ PushOneCommit.Result c1 = pushCommit("master", "commitmsg1", "file1", "content1");
+ testRepo.reset(initialHead);
+ PushOneCommit.Result c2 = pushCommit("master", "commitmsg2", "file2", "content2");
+ testRepo.reset(initialHead);
+ PushOneCommit.Result c3 = pushCommit("master", "commitmsg3", "file3", "content3");
+
+ approve(c1.getChangeId());
+ approve(c2.getChangeId());
+ approve(c3.getChangeId());
+
+ String changes;
+ changes = String.valueOf(c1.getPatchSetId().getParentKey().get()) + "," + c1.getPatchSetId().getId();
+ changes += " " + String.valueOf(c2.getPatchSetId().getParentKey().get()) + "," + c2.getPatchSetId().getId();
+ changes += " " + String.valueOf(c3.getPatchSetId().getParentKey().get()) + "," + c3.getPatchSetId().getId();
+ String result = qtSshStageCommon(c1, changes, false);
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void error_InvalidPatch() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+ approve(c.getChangeId());
+
+ String changes = "invalid";
+ String result = qtSshStageCommon(c, changes, true);
+ assertThat(result).contains("is not a valid patch set");
+ }
+
+ @Test
+ public void error_ChangeNotFound() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+ approve(c.getChangeId());
+
+ String changes = "9999,1";
+ String result = qtSshStageCommon(c, changes, true);
+ assertThat(result).contains("no such change");
+ }
+
+ @Test
+ public void error_PatchNotFound() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+ approve(c.getChangeId());
+ String changes = String.valueOf(c.getPatchSetId().getParentKey().get()) +",9999";
+ String result = qtSshStageCommon(c, changes, true);
+ assertThat(result).contains("no such patch set");
+ }
+
+ @Test
+ public void error_PatchNotCurrent() throws Exception {
+ PushOneCommit.Result c1 = pushCommit("master", "commitmsg1", "file1", "content1");
+ PushOneCommit.Result c2 = amendCommit(c1.getChangeId());
+ approve(c2.getChangeId());
+
+ String changes = String.valueOf(c2.getPatchSetId().getParentKey().get()) +",1";
+ String result = qtSshStageCommon(c2, changes, true);
+ assertThat(result).contains("is not current");
+ }
+
+ @Test
+ public void error_Wrong_Status() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+ approve(c.getChangeId());
+
+ grant(project, "refs/heads/master", Permission.ABANDON, false, REGISTERED_USERS);
+ QtDefer(c);
+ deny(project, "refs/heads/master", Permission.ABANDON, REGISTERED_USERS);
+
+ String result = qtSshStageExpectFail(c);
+ assertThat(result).contains("Change is DEFERRED");
+ }
+
+ @Test
+ public void error_NoPermission() throws Exception {
+ deny(project, "refs/heads/master", Permission.QT_STAGE, REGISTERED_USERS);
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+ approve(c.getChangeId());
+
+ String result = qtSshStageExpectFail(c);
+ assertThat(result).contains("not permitted");
+ grant(project, "refs/heads/master", Permission.QT_STAGE, false, REGISTERED_USERS);
+ }
+
+ @Test
+ public void error_notReviewed() throws Exception {
+ PushOneCommit.Result c = pushCommit("master", "commitmsg1", "file1", "content1");
+
+ String result = qtSshStageExpectFail(c);
+ assertThat(result).contains("needs Code-Review");
+ }
+
+ private void qtSshStage(PushOneCommit.Result c) throws Exception {
+ qtSshStageCommon(c, false);
+ }
+
+ private String qtSshStageExpectFail(PushOneCommit.Result c) throws Exception {
+ return qtSshStageCommon(c, true);
+ }
+
+ private String qtSshStageCommon(PushOneCommit.Result c,
+ Boolean expectFail)
+ throws Exception {
+ String changes;
+ changes = String.valueOf(c.getPatchSetId().getParentKey().get()) + ",";
+ changes += c.getPatchSetId().getId();
+
+ return qtSshStageCommon(c, changes, expectFail);
+ }
+
+ private String qtSshStageCommon(PushOneCommit.Result c,
+ String changes,
+ Boolean expectFail)
+ throws Exception {
+ String commandStr ="gerrit-plugin-qt-workflow stage " + changes;
+
+ String resultStr = adminSshSession.exec(commandStr);
+ if (expectFail) assertThat(adminSshSession.getError()).isNotNull();
+ else assertThat(adminSshSession.getError()).isNull();
+
+ Change change = c.getChange().change();
+ if (expectFail) assertThat(change.getStatus()).isNotEqualTo(Change.Status.STAGED);
+ else assertThat(change.getStatus()).isEqualTo(Change.Status.STAGED);
+
+ return adminSshSession.getError();
+ }
+
+}