diff options
author | Mika Hamalainen <mika.hamalainen@accenture.com> | 2011-09-02 14:39:28 +0300 |
---|---|---|
committer | Mika Hamalainen <mika.hamalainen@accenture.com> | 2011-09-02 15:25:54 +0300 |
commit | f94b1355d7066b0fb3194a4d98be6dc138f83386 (patch) | |
tree | ba0030a7d8d7ac7df08357fc01b18fac3d04ac60 | |
parent | 7cf342c74c77ea88fc0de1512de1fdf1a09d84a0 (diff) |
Notification e-mails when build is approved
Added e-mail notifications for approved and rejected builds. This
includes new e-mail templates and sender classes. The e-mails are
send from the staging approve command.
Change-Id: I159c166ed1e62ecddfbfea0155d6ca278afe1eb7
6 files changed, 340 insertions, 0 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java index e98b49b509..42f145d2e0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java @@ -30,6 +30,8 @@ import com.google.gerrit.server.git.StagingMergeDelegate; import com.google.gerrit.server.git.SubmitMergeDelegate; import com.google.gerrit.server.mail.AbandonedSender; import com.google.gerrit.server.mail.AddReviewerSender; +import com.google.gerrit.server.mail.BuildApprovedSender; +import com.google.gerrit.server.mail.BuildRejectedSender; import com.google.gerrit.server.mail.CommentSender; import com.google.gerrit.server.mail.CreateChangeSender; import com.google.gerrit.server.mail.MergeFailSender; @@ -83,5 +85,7 @@ public class GerritRequestModule extends FactoryModule { factory(MergeFailSender.Factory.class); factory(RegisterNewEmailSender.Factory.class); factory(PerformCreateGroup.Factory.class); + factory(BuildApprovedSender.Factory.class); + factory(BuildRejectedSender.Factory.class); } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/BuildApprovedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/BuildApprovedSender.java new file mode 100644 index 0000000000..f4ec23f166 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/BuildApprovedSender.java @@ -0,0 +1,156 @@ +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.mail; + +import com.google.gerrit.common.data.ApprovalType; +import com.google.gerrit.common.data.ApprovalTypes; +import com.google.gerrit.reviewdb.Account; +import com.google.gerrit.reviewdb.AccountProjectWatch; +import com.google.gerrit.reviewdb.AccountProjectWatch.NotifyType; +import com.google.gerrit.reviewdb.ApprovalCategory; +import com.google.gerrit.reviewdb.ApprovalCategoryValue; +import com.google.gerrit.reviewdb.Change; +import com.google.gerrit.reviewdb.PatchSetApproval; +import com.google.gwtorm.client.OrmException; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; + +import java.util.HashMap; +import java.util.Map; + +/** Send notice about a change successfully merged. */ +public class BuildApprovedSender extends ReplyToChangeSender { + public static interface Factory { + public BuildApprovedSender create(Change change); + } + + private final ApprovalTypes approvalTypes; + + @Inject + public BuildApprovedSender(EmailArguments ea, ApprovalTypes at, @Assisted Change c) { + super(ea, c, "build-approved"); + approvalTypes = at; + } + + @Override + protected void init() throws EmailException { + super.init(); + + ccAllApprovals(); + bccStarredBy(); + bccWatchesNotifyAllComments(); + bccWatchesNotifySubmittedChanges(); + } + + @Override + protected void formatChange() throws EmailException { + appendText(velocifyFile("BuildApproved.vm")); + } + + public String getApprovals() { + try { + final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> pos = + new HashMap<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>>(); + + final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> neg = + new HashMap<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>>(); + + for (PatchSetApproval ca : args.db.get().patchSetApprovals() + .byPatchSet(patchSet.getId())) { + if (ca.getValue() > 0) { + insert(pos, ca); + } else if (ca.getValue() < 0) { + insert(neg, ca); + } + } + + return format("Approvals", pos) + format("Objections", neg); + } catch (OrmException err) { + // Don't list the approvals + } + return ""; + } + + private String format(final String type, + final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> list) { + StringBuilder txt = new StringBuilder(); + if (list.isEmpty()) { + return ""; + } + txt.append(type + ":\n"); + for (final Map.Entry<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> ent : list + .entrySet()) { + final Map<ApprovalCategory.Id, PatchSetApproval> l = ent.getValue(); + txt.append(" "); + txt.append(getNameFor(ent.getKey())); + txt.append(": "); + boolean first = true; + for (ApprovalType at : approvalTypes.getApprovalTypes()) { + final PatchSetApproval ca = l.get(at.getCategory().getId()); + if (ca == null) { + continue; + } + + if (first) { + first = false; + } else { + txt.append("; "); + } + + final ApprovalCategoryValue v = at.getValue(ca); + if (v != null) { + txt.append(v.getName()); + } else { + txt.append(at.getCategory().getName()); + txt.append("="); + if (ca.getValue() > 0) { + txt.append("+"); + } + txt.append("" + ca.getValue()); + } + } + txt.append("\n"); + } + txt.append("\n"); + return txt.toString(); + } + + private void insert( + final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> list, + final PatchSetApproval ca) { + Map<ApprovalCategory.Id, PatchSetApproval> m = list.get(ca.getAccountId()); + if (m == null) { + m = new HashMap<ApprovalCategory.Id, PatchSetApproval>(); + list.put(ca.getAccountId(), m); + } + m.put(ca.getCategoryId(), ca); + } + + private void bccWatchesNotifySubmittedChanges() { + try { + // BCC anyone else who has interest in this project's changes + // + for (final AccountProjectWatch w : getWatches()) { + if (w.isNotify(NotifyType.SUBMITTED_CHANGES)) { + add(RecipientType.BCC, w.getAccountId()); + } + } + } catch (OrmException err) { + // Just don't CC everyone. Better to send a partial message to those + // we already have queued up then to fail deliver entirely to people + // who have a lower interest in the change. + } + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/BuildRejectedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/BuildRejectedSender.java new file mode 100644 index 0000000000..29516a2b76 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/BuildRejectedSender.java @@ -0,0 +1,43 @@ +// Copyright (C) 2009 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.mail; + +import com.google.gerrit.reviewdb.Change; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; + +/** Send notice about a change failing to merged. */ +public class BuildRejectedSender extends ReplyToChangeSender { + public static interface Factory { + public BuildRejectedSender create(Change change); + } + + @Inject + public BuildRejectedSender(EmailArguments ea, @Assisted Change c) { + super(ea, c, "build-rejected"); + } + + @Override + protected void init() throws EmailException { + super.init(); + + ccExistingReviewers(); + } + + @Override + protected void formatChange() throws EmailException { + appendText(velocifyFile("BuildRejected.vm")); + } +} diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/BuildApproved.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/BuildApproved.vm new file mode 100644 index 0000000000..4d05422c9e --- /dev/null +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/BuildApproved.vm @@ -0,0 +1,44 @@ +## Copyright (C) 2011 The Android Open Source Project +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +## +## Template Type: +## ------------- +## This is a velocity mail template, see: http://velocity.apache.org and the +## gerrit-docs:config-mail.txt for more info on modifying gerrit mail templates. +## +## Template File Names and extensions: +## ---------------------------------- +## Gerrit will use templates ending in ".vm" but will ignore templates ending +## in ".vm.example". If a .vm template does not exist, the default internal +## gerrit template which is the same as the .vm.example will be used. If you +## want to override the default template, copy the .vm.exmaple file to a .vm +## file and edit it appropriately. +## +## This Template: +## -------------- +## The Merged.vm template will determine the contents of the email related to +## a change successfully merged to the head. It is a ChangeEmail: see +## ChangeSubject.vm and ChangeFooter.vm. +## +#macro(elipses $length $str) +#if($str.length() > $length)${str.substring(0,$length)}...#else$str#end +#end +$fromName has approved a build with this change and it was merged. + +Change subject: $change.subject +...................................................................... + + +$email.changeDetail$email.approvals diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/BuildRejected.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/BuildRejected.vm new file mode 100644 index 0000000000..c72a34a9cd --- /dev/null +++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/BuildRejected.vm @@ -0,0 +1,44 @@ +## Copyright (C) 2011 The Android Open Source Project +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +## +## Template Type: +## ------------- +## This is a velocity mail template, see: http://velocity.apache.org and the +## gerrit-docs:config-mail.txt for more info on modifying gerrit mail templates. +## +## Template File Names and extensions: +## ---------------------------------- +## Gerrit will use templates ending in ".vm" but will ignore templates ending +## in ".vm.example". If a .vm template does not exist, the default internal +## gerrit template which is the same as the .vm.example will be used. If you +## want to override the default template, copy the .vm.exmaple file to a .vm +## file and edit it appropriately. +## +## This Template: +## -------------- +## The MergeFail.vm template will determine the contents of the email related +## to a failure upon attempting to merge a change to the head. It is a +## ChangeEmail: see ChangeSubject.vm and ChangeFooter.vm. +## +$fromName has REJECTED a build with this change and it needs attention. + +Change subject: $change.subject +...................................................................... + + +#if ($email.coverLetter) +$email.coverLetter + +#end diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StagingApprove.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StagingApprove.java index 2ec11326fd..11361a811e 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StagingApprove.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StagingApprove.java @@ -32,6 +32,9 @@ import com.google.gerrit.server.TopicUtil; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.MergeOp; import com.google.gerrit.server.git.MergeQueue; +import com.google.gerrit.server.mail.BuildApprovedSender; +import com.google.gerrit.server.mail.BuildRejectedSender; +import com.google.gerrit.server.mail.EmailException; import com.google.gerrit.server.patch.PublishComments; import com.google.gerrit.server.project.CanSubmitResult; import com.google.gerrit.server.project.ChangeControl; @@ -57,6 +60,8 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevSort; import org.eclipse.jgit.revwalk.RevWalk; import org.kohsuke.args4j.Option; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.IOException; @@ -75,6 +80,8 @@ import java.util.List; * $ ssh -p 29418 localhost gerrit staging-approve -p project -b master -i 123 -r=pass */ public class StagingApprove extends BaseCommand { + private static final Logger log = + LoggerFactory.getLogger(StagingApprove.class); private class MergeException extends Exception { private static final long serialVersionUID = 1L; @@ -131,6 +138,12 @@ public class StagingApprove extends BaseCommand { @Inject private TopicFunctionState.Factory topicFunctionStateFactory; + @Inject + private BuildApprovedSender.Factory buildApprovedFactory; + + @Inject + private BuildRejectedSender.Factory buildRejectedFactory; + @Option(name = "--project", aliases = {"-p"}, required = true, usage = "project name") private String project; @@ -365,8 +378,18 @@ public class StagingApprove extends BaseCommand { case FAST_FORWARD: hooks.doRefUpdatedHook(destination, branchUpdate, currentUser.getAccount()); + try { + sendBuildApprovedMails(); + } catch (Exception e) { + log.error("Failed to send change merged e-mails", e); + } break; default: + try { + sendBuildRejectedMails(); + } catch (Exception e) { + log.error("Failed to send change merged e-mails", e); + } throw new MergeException("Could not fast-forward build to destination branch"); } } finally { @@ -455,4 +478,30 @@ public class StagingApprove extends BaseCommand { throw new MergeException("fatal: Failed to rebuild staging branch after failed fast-forward", e); } } + + private void sendBuildApprovedMails() throws OrmException, EmailException { + for (PatchSet patchSet : toApprove) { + final PatchSet.Id patchSetId = patchSet.getId(); + final Change.Id changeId = patchSetId.getParentKey(); + final Change change = db.changes().get(changeId); + + final BuildApprovedSender sender = buildApprovedFactory.create(change); + sender.setFrom(currentUser.getAccountId()); + sender.setPatchSet(patchSet); + sender.send(); + } + } + + private void sendBuildRejectedMails() throws OrmException, EmailException { + for (PatchSet patchSet : toApprove) { + final PatchSet.Id patchSetId = patchSet.getId(); + final Change.Id changeId = patchSetId.getParentKey(); + final Change change = db.changes().get(changeId); + + final BuildRejectedSender sender = buildRejectedFactory.create(change); + sender.setFrom(currentUser.getAccountId()); + sender.setPatchSet(patchSet); + sender.send(); + } + } } |