Add auto assign plugin.
Depends on gerrit-owners for parsing the OWNERS files and then will
automatically set the reviewers whenever a change is created or updated.
It will only add new reviewers, never removing old ones.
diff --git a/README.md b/README.md
index ecd9d35..b722578 100644
--- a/README.md
+++ b/README.md
@@ -43,3 +43,8 @@
gerrit_owners:add_owner_approval(Approvers, Ds, A),
S =.. [submit | A].
```
+
+## Auto assigner
+
+There is a second plugin, `gerrit-owners-autoassign` which depends on `gerrit-owners`. It will automatically assign
+all of the owners to review a change when it's created or updated.
diff --git a/gerrit-owners-autoassign/pom.xml b/gerrit-owners-autoassign/pom.xml
new file mode 100644
index 0000000..e23dbec
--- /dev/null
+++ b/gerrit-owners-autoassign/pom.xml
@@ -0,0 +1,97 @@
+<!--
+Copyright (c) 2013 VMware, Inc. All Rights Reserved.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.vmware.gerrit</groupId>
+ <artifactId>gerrit-owners-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-owners-autoassign</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-SNAPSHOT</version>
+ <name>gerrit-owners-autoassign</name>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.4</version>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <configuration>
+ <createDependencyReducedPom>false</createDependencyReducedPom>
+ <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+ <artifactSet>
+ <excludes>
+ <exclude>com.gerritforge:*</exclude>
+ <exclude>com.google.gerrit:*</exclude>
+ <exclude>org.bouncycastle:*</exclude>
+ <exclude>org.slf4j:*</exclude>
+ <exclude>com.google.guava:*</exclude>
+ <exclude>org.eclipse.jgit:*</exclude>
+ <exclude>wsdl4j:wsdl4j:*</exclude>
+ </excludes>
+ </artifactSet>
+ <transformers>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <manifestEntries>
+ <Implementation-Vendor>VMware, Inc.</Implementation-Vendor>
+ <Implementation-URL>http://github.com/vadims/gerrit-owners</Implementation-URL>
+ <Implementation-Title>${Gerrit-ApiType} ${project.artifactId}</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ <Gerrit-ApiType>${Gerrit-ApiType}</Gerrit-ApiType>
+ <Gerrit-ApiVersion>${project.version}</Gerrit-ApiVersion>
+ </manifestEntries>
+ </transformer>
+ </transformers>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-${Gerrit-ApiType}-api</artifactId>
+ <version>${Gerrit-ApiVersion}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.vmware.gerrit</groupId>
+ <artifactId>gerrit-owners</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/GitRefListener.java b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/GitRefListener.java
new file mode 100644
index 0000000..fcda396
--- /dev/null
+++ b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/GitRefListener.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2013 VMware, Inc. All Rights Reserved.
+ */
+package com.vmware.gerrit.owners.autoassigner;
+
+import com.vmware.gerrit.owners.PathOwners;
+
+import com.google.gerrit.extensions.annotations.Listen;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.patch.PatchList;
+import com.google.gerrit.server.patch.PatchListCache;
+import com.google.gerrit.server.patch.PatchListKey;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Provider;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.util.List;
+
+import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_NONE;
+
+@Listen
+public class GitRefListener implements GitReferenceUpdatedListener {
+ private static final Logger logger = LoggerFactory.getLogger(GitRefListener.class);
+
+ private static final String CHANGES_REF = "refs/changes/";
+
+ private final Provider<ReviewDb> db;
+
+ private final PatchListCache patchListCache;
+
+ private final GitRepositoryManager repositoryManager;
+
+ private final AccountResolver accountResolver;
+
+ private final ReviewerManager reviewerManager;
+
+ @Inject
+ public GitRefListener(Provider<ReviewDb> db,
+ PatchListCache patchListCache,
+ GitRepositoryManager repositoryManager,
+ AccountResolver accountResolver,
+ ReviewerManager reviewerManager) {
+ this.db = db;
+ this.patchListCache = patchListCache;
+ this.repositoryManager = repositoryManager;
+ this.accountResolver = accountResolver;
+ this.reviewerManager = reviewerManager;
+ }
+
+ @Override
+ public void onGitReferenceUpdated(Event event) {
+ String projectName = event.getProjectName();
+ Repository repository;
+ try {
+ repository = repositoryManager.openRepository(Project.NameKey.parse(projectName));
+ try {
+ processUpdates(repository, event.getUpdates());
+ } finally {
+ repository.close();
+ }
+ } catch (IOException e) {
+ logger.warn("Couldn't open repository: {}", projectName, e);
+ }
+ }
+
+ private void processUpdates(Repository repository, List<Update> updates) {
+ for (Update update : updates) {
+ if (update.getRefName().startsWith(CHANGES_REF)) {
+ Change.Id id = Change.Id.fromRef(update.getRefName());
+ try {
+ Change change = db.get().changes().get(id);
+ PatchList patchList = getPatchList(update, change);
+ if (patchList != null) {
+ PathOwners owners = new PathOwners(accountResolver, repository, patchList);
+ reviewerManager.addReviewers(change, owners.get().values());
+ }
+ } catch (OrmException e) {
+ logger.warn("Could not open change: {}", id, e);
+ } catch (ReviewerManagerException e) {
+ logger.warn("Could not add reviewers for change: {}", id, e);
+ }
+ }
+ }
+ }
+
+ private PatchList getPatchList(Update update, Change change) {
+ ObjectId newId = null;
+ if (update.getNewObjectId() != null) {
+ newId = ObjectId.fromString(update.getNewObjectId());
+ }
+
+ PatchListKey plKey = new PatchListKey(change.getProject(), null, newId, IGNORE_NONE);
+ try {
+ return patchListCache.get(plKey);
+ } catch (PatchListNotAvailableException e) {
+ logger.warn("Could not load patch list: {}", plKey, e);
+ }
+ return null;
+ }
+}
diff --git a/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManager.java b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManager.java
new file mode 100644
index 0000000..8e547be
--- /dev/null
+++ b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManager.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013 VMware, Inc. All Rights Reserved.
+ */
+package com.vmware.gerrit.owners.autoassigner;
+
+import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.PostReviewers;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.server.OrmException;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import java.util.Collection;
+
+@Singleton
+public class ReviewerManager {
+
+ private final Provider<CurrentUser> currentUserProvider;
+
+ private final Provider<PostReviewers> postReviewersProvider;
+
+ private final ChangeControl.GenericFactory changeControlFactory;
+
+ @Inject
+ public ReviewerManager(Provider<CurrentUser> currentUserProvider,
+ Provider<PostReviewers> postReviewersProvider,
+ ChangeControl.GenericFactory changeControlFactory) {
+ this.currentUserProvider = currentUserProvider;
+ this.postReviewersProvider = postReviewersProvider;
+ this.changeControlFactory = changeControlFactory;
+ }
+
+ public void addReviewers(Change change, Collection<Account.Id> reviewers) throws ReviewerManagerException {
+ try {
+ PostReviewers postReviewers = postReviewersProvider.get();
+ ChangeControl changeControl = changeControlFactory.controlFor(change, currentUserProvider.get());
+ ChangeResource changeResource = new ChangeResource(changeControl);
+
+ // HACK(vspivak): Using PostReviewers is probably inefficient here, however it has all the hook/notification
+ // logic, so it's easier to call it then to mimic/copy the logic here.
+ for (Account.Id accountId : reviewers) {
+ PostReviewers.Input input = new PostReviewers.Input();
+ input.reviewer = accountId.toString();
+ postReviewers.apply(changeResource, input);
+ }
+ } catch (RestApiException e) {
+ throw new ReviewerManagerException(e);
+ } catch (NoSuchChangeException e) {
+ throw new ReviewerManagerException(e);
+ } catch (EmailException e) {
+ throw new ReviewerManagerException(e);
+ } catch (OrmException e) {
+ throw new ReviewerManagerException(e);
+ }
+ }
+}
diff --git a/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManagerException.java b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManagerException.java
new file mode 100644
index 0000000..a5aecfe
--- /dev/null
+++ b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManagerException.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2013 VMware, Inc. All Rights Reserved.
+ */
+package com.vmware.gerrit.owners.autoassigner;
+
+public class ReviewerManagerException extends Exception {
+ public ReviewerManagerException(String message) {
+ super(message);
+ }
+
+ public ReviewerManagerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ReviewerManagerException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/pom.xml b/pom.xml
index 525455e..367106a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,7 @@
<modules>
<module>gerrit-owners</module>
+ <module>gerrit-owners-autoassign</module>
</modules>
<build>