Add --json option to review SSH command

Add --json option to review SSH command for posting code review,
message and inline comments via SSH.  Command reads the review
input from text file in JSON format, that corresponds to REST's
ReviewInput entity:

  $ cat review.json | ssh host gerrit review --json <SHA1>

Bug: Issue 602
Change-Id: I7e6f84b878b835cb7ea144cddd99702116d69b57
This commit is contained in:
Mani Chandel 2014-07-04 17:50:51 +05:30
parent e985c14c30
commit 42a7d2dc64
2 changed files with 60 additions and 9 deletions

View File

@ -14,6 +14,7 @@ gerrit review - Apply reviews to one or more patch sets
[--submit | -s]
[--abandon | --restore]
[--publish]
[--json | -j]
[--delete]
[--verified <N>] [--code-review <N>]
[--label Label-Name=<N>]
@ -56,6 +57,12 @@ branch.
Optional cover letter to include as part of the message
sent to reviewers when the approval states are updated.
--json::
-j::
Read review input from JSON file. See
link:rest-api-changes.html#review-input[ReviewInput] entity for the
format.
--notify::
-n::
Who to send email notifications to after the review is stored.

View File

@ -16,6 +16,7 @@ package com.google.gerrit.sshd.commands;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.io.CharStreams;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.extensions.api.GerritApi;
@ -30,8 +31,8 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
@ -39,6 +40,7 @@ import com.google.gerrit.server.util.LabelVote;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.gerrit.util.cli.CmdLineParser;
import com.google.gson.JsonSyntaxException;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
@ -50,6 +52,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@ -110,6 +114,9 @@ public class ReviewCommand extends SshCommand {
@Option(name = "--delete", usage = "delete the specified draft patch set(s)")
private boolean deleteDraftPatchSet;
@Option(name = "--json", aliases = "-j", usage = "read review input json from stdin")
private boolean json;
@Option(name = "--label", aliases = "-l", usage = "custom label(s) to assign", metaVar = "LABEL=VALUE")
void addLabel(final String token) {
LabelVote v = LabelVote.parseWithEquals(token);
@ -159,12 +166,41 @@ public class ReviewCommand extends SshCommand {
throw error("publish and delete actions are mutually exclusive");
}
}
if (json) {
if (restoreChange) {
throw error("json and restore actions are mutually exclusive");
}
if (submitChange) {
throw error("json and submit actions are mutually exclusive");
}
if (deleteDraftPatchSet) {
throw error("json and delete actions are mutually exclusive");
}
if (publishPatchSet) {
throw error("json and publish actions are mutually exclusive");
}
if (abandonChange) {
throw error("json and abandon actions are mutually exclusive");
}
if (changeComment != null) {
throw error("json and message are mutually exclusive");
}
}
boolean ok = true;
ReviewInput input = null;
if (json) {
input = reviewFromJson();
}
for (final PatchSet patchSet : patchSets) {
try {
reviewPatchSet(patchSet);
} catch (UnloggedFailure e) {
if (input != null) {
applyReview(patchSet, input);
} else {
reviewPatchSet(patchSet);
}
} catch (RestApiException | UnloggedFailure e) {
ok = false;
writeError("error: " + e.getMessage() + "\n");
} catch (NoSuchChangeException e) {
@ -179,21 +215,30 @@ public class ReviewCommand extends SshCommand {
}
if (!ok) {
throw new UnloggedFailure(1, "one or more reviews failed;"
+ " review output above");
throw error("one or more reviews failed; review output above");
}
}
private void applyReview(PatchSet patchSet,
final ReviewInput review) throws Exception {
final ReviewInput review) throws RestApiException {
gApi.get().changes()
.id(patchSet.getId().getParentKey().get())
.revision(patchSet.getRevision().get())
.review(review);
}
private void reviewPatchSet(final PatchSet patchSet) throws Exception {
private ReviewInput reviewFromJson() throws UnloggedFailure {
try (InputStreamReader r =
new InputStreamReader(in, StandardCharsets.UTF_8)) {
return OutputFormat.JSON.newGson().
fromJson(CharStreams.toString(r), ReviewInput.class);
} catch (IOException | JsonSyntaxException e) {
writeError(e.getMessage() + '\n');
throw error("internal error while reading review input");
}
}
private void reviewPatchSet(final PatchSet patchSet) throws Exception {
if (changeComment == null) {
changeComment = "";
}
@ -243,8 +288,7 @@ public class ReviewCommand extends SshCommand {
} else if (deleteDraftPatchSet) {
revisionApi(patchSet).delete();
}
} catch (IllegalStateException | InvalidChangeOperationException
| RestApiException e) {
} catch (IllegalStateException | RestApiException e) {
throw error(e.getMessage());
}
}