diff options
author | Jukka Jokiniva <jukka.jokiniva@qt.io> | 2019-05-22 17:26:03 +0300 |
---|---|---|
committer | Jukka Jokiniva <jukka.jokiniva@qt.io> | 2019-05-23 07:07:23 +0000 |
commit | 6b6abb74af3d76491b3658c672dde2593d3e23e3 (patch) | |
tree | 33f81a872430f416df4fb4add60d15c04ca5b3d6 | |
parent | ef970bc31996f781070f7079c4fd731993101bb6 (diff) |
Add ssh command api for staging
Fixes: QTQAINFRA-2989
Change-Id: I204153146a5c321fcc87dbabcf8b180264b897e4
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io>
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(); + } + +} |