From 42a7d2dc640ba2a378d019f37f1745ddc40544ab Mon Sep 17 00:00:00 2001 From: Mani Chandel Date: Fri, 4 Jul 2014 17:50:51 +0530 Subject: [PATCH] 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 Bug: Issue 602 Change-Id: I7e6f84b878b835cb7ea144cddd99702116d69b57 --- Documentation/cmd-review.txt | 7 +++ .../gerrit/sshd/commands/ReviewCommand.java | 62 ++++++++++++++++--- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/Documentation/cmd-review.txt b/Documentation/cmd-review.txt index 787c70ccc8..5eea379a00 100644 --- a/Documentation/cmd-review.txt +++ b/Documentation/cmd-review.txt @@ -14,6 +14,7 @@ gerrit review - Apply reviews to one or more patch sets [--submit | -s] [--abandon | --restore] [--publish] + [--json | -j] [--delete] [--verified ] [--code-review ] [--label Label-Name=] @@ -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. diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java index da393577a0..26507b367a 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java @@ -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()); } }