Add the ability to run a new project created hook
Notify users when a new project has been created. This could solve
an issue in the Jenkins gerrit-trigger-plugin where we need to
continuously fetch the whole project list (using the command
gerrit ls-projects) to get project name auto completion working
in the Project Configuration pages. By letting the plug-in pickup
new projects on the fly, our auto completion would be up-to-date
much quicker and also drain less resources from Gerrit.
The hook takes the following form:
project-created --project <project name> --head <head name>
Change-Id: Ibf53946b12df4efd2f929fa7fc6d23499ed7ed88
diff --git a/Documentation/cmd-stream-events.txt b/Documentation/cmd-stream-events.txt
index c754f35..dcdbb07 100644
--- a/Documentation/cmd-stream-events.txt
+++ b/Documentation/cmd-stream-events.txt
@@ -153,6 +153,19 @@
eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
created.
+=== Project Created
+
+Sent when a new project has been created.
+
+type:: "project-created"
+
+projectName:: The created project name
+
+projectHead:: The created project head name
+
+eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
+created.
+
=== Merge Failed
Sent when a change has failed to be merged into the git repository.
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index dab0f5e..742d996 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1832,6 +1832,11 @@
Optional filename for the hashtags changed hook, if not specified then
`hashtags-changed` will be used.
+[[hooks.projectCreatedHook]]hooks.projectCreatedHook::
++
+Optional filename for the project created hook, if not specified then
+`project-created` will be used.
+
[[hooks.mergeFailedHook]]hooks.mergeFailedHook::
+
Optional filename for the merge failed hook, if not specified then
diff --git a/Documentation/config-hooks.txt b/Documentation/config-hooks.txt
index 5666dff..d4f53bf 100644
--- a/Documentation/config-hooks.txt
+++ b/Documentation/config-hooks.txt
@@ -110,6 +110,14 @@
ref-updated --oldrev <old rev> --newrev <new rev> --refname <ref name> --project <project name> --submitter <submitter>
====
+=== project-created
+
+Called whenever a project has been created.
+
+====
+ project-created --project <project name> --head <head name>
+====
+
=== reviewer-added
Called whenever a reviewer is added to a change.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
index 3a58373..19c3145 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -20,6 +20,7 @@
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.extensions.events.NewProjectCreatedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.reviewdb.client.Account;
@@ -40,11 +41,11 @@
import com.google.gerrit.server.events.ChangeRestoredEvent;
import com.google.gerrit.server.events.CommentAddedEvent;
import com.google.gerrit.server.events.DraftPublishedEvent;
-import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.EventFactory;
import com.google.gerrit.server.events.HashtagsChangedEvent;
import com.google.gerrit.server.events.MergeFailedEvent;
import com.google.gerrit.server.events.PatchSetCreatedEvent;
+import com.google.gerrit.server.events.ProjectCreatedEvent;
import com.google.gerrit.server.events.RefUpdatedEvent;
import com.google.gerrit.server.events.ReviewerAddedEvent;
import com.google.gerrit.server.events.TopicChangedEvent;
@@ -89,7 +90,7 @@
/** Spawns local executables when a hook action occurs. */
@Singleton
public class ChangeHookRunner implements ChangeHooks, EventDispatcher,
- EventSource, LifecycleListener {
+ EventSource, LifecycleListener, NewProjectCreatedListener {
/** A logger for this class. */
private static final Logger log = LoggerFactory.getLogger(ChangeHookRunner.class);
@@ -100,6 +101,7 @@
bind(ChangeHooks.class).to(ChangeHookRunner.class);
bind(EventDispatcher.class).to(ChangeHookRunner.class);
bind(EventSource.class).to(ChangeHookRunner.class);
+ DynamicSet.bind(binder(), NewProjectCreatedListener.class).to(ChangeHookRunner.class);
listener().to(ChangeHookRunner.class);
}
}
@@ -209,6 +211,9 @@
/** Path of the hashtags changed hook */
private final Path hashtagsChangedHook;
+ /** Path of the project created hook. */
+ private final Path projectCreatedHook;
+
private final String anonymousCowardName;
/** Repository Manager. */
@@ -282,6 +287,7 @@
claSignedHook = hook(config, hooksPath, "cla-signed");
refUpdateHook = hook(config, hooksPath, "ref-update");
hashtagsChangedHook = hook(config, hooksPath, "hashtags-changed");
+ projectCreatedHook = hook(config, hooksPath, "project-created");
syncHookTimeout = config.getInt("hooks", "syncHookTimeout", 30);
syncHookThreadPool = Executors.newCachedThreadPool(
@@ -346,6 +352,20 @@
return runSyncHook(project.getNameKey(), refUpdateHook, args);
}
+ @Override
+ public void doProjectCreatedHook(Project.NameKey project, String headName) {
+ ProjectCreatedEvent event = new ProjectCreatedEvent();
+ event.projectName = project.get();
+ event.headName = headName;
+ fireEvent(project, event);
+
+ List<String> args = new ArrayList<>();
+ addArg(args, "--project", project.get());
+ addArg(args, "--head", headName);
+
+ runHook(project, projectCreatedHook, args);
+ }
+
/**
* Fire the Patchset Created Hook.
*
@@ -695,24 +715,24 @@
}
@Override
- public void postEvent(Change change, Event event, ReviewDb db)
- throws OrmException {
+ public void postEvent(Change change, com.google.gerrit.server.events.Event event,
+ ReviewDb db) throws OrmException {
fireEvent(change, event, db);
}
@Override
- public void postEvent(Branch.NameKey branchName, Event event) {
+ public void postEvent(Branch.NameKey branchName, com.google.gerrit.server.events.Event event) {
fireEvent(branchName, event);
}
- private void fireEventForUnrestrictedListeners(Event event) {
+ private void fireEventForUnrestrictedListeners(com.google.gerrit.server.events.Event event) {
for (EventListener listener : unrestrictedListeners) {
listener.onEvent(event);
}
}
- private void fireEvent(Change change, Event event, ReviewDb db)
- throws OrmException {
+ private void fireEvent(Change change, com.google.gerrit.server.events.Event event,
+ ReviewDb db) throws OrmException {
for (EventListenerHolder holder : listeners.values()) {
if (isVisibleTo(change, holder.user, db)) {
holder.listener.onEvent(event);
@@ -722,7 +742,32 @@
fireEventForUnrestrictedListeners( event );
}
- private void fireEvent(Branch.NameKey branchName, Event event) {
+ private void fireEvent(Project.NameKey project, ProjectCreatedEvent event) {
+ for (EventListenerHolder holder : listeners.values()) {
+ if (isVisibleTo(project, event, holder.user)) {
+ holder.listener.onEvent(event);
+ }
+ }
+
+ fireEventForUnrestrictedListeners(event);
+ }
+
+ private void fireEventForUnrestrictedListeners(ProjectCreatedEvent event) {
+ for (EventListener listener : unrestrictedListeners) {
+ listener.onEvent(event);
+ }
+ }
+
+ private boolean isVisibleTo(Project.NameKey project, ProjectCreatedEvent event, CurrentUser user) {
+ ProjectState pe = projectCache.get(project);
+ if (pe == null) {
+ return false;
+ }
+ ProjectControl pc = pe.controlFor(user);
+ return pc.controlForRef(event.getHeadName()).isVisible();
+ }
+
+ private void fireEvent(Branch.NameKey branchName, com.google.gerrit.server.events.Event event) {
for (EventListenerHolder holder : listeners.values()) {
if (isVisibleTo(branchName, holder.user)) {
holder.listener.onEvent(event);
@@ -995,4 +1040,10 @@
super.runHook();
}
}
+
+ @Override
+ public void onNewProjectCreated(NewProjectCreatedListener.Event event) {
+ Project.NameKey project = new Project.NameKey(event.getProjectName());
+ doProjectCreatedHook(project, event.getHeadName());
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java
index 7f7e8b2..b16a8a5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHooks.java
@@ -181,4 +181,12 @@
public void doHashtagsChangedHook(Change change, Account account,
Set<String>added, Set<String> removed, Set<String> hashtags,
ReviewDb db) throws OrmException;
+
+ /**
+ * Fire the project created hook
+ *
+ * @param project The project that was created
+ * @param headName The head name of the created project
+ */
+ public void doProjectCreatedHook(Project.NameKey project, String headName);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java b/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java
index 156672e..bed77a7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/DisabledChangeHooks.java
@@ -114,6 +114,10 @@
}
@Override
+ public void doProjectCreatedHook(Project.NameKey project, String headName) {
+ }
+
+ @Override
public void postEvent(Change change, Event event, ReviewDb db) {
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventTypes.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventTypes.java
index 908fd0a..9b37c38 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventTypes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventTypes.java
@@ -35,6 +35,7 @@
registerClass(new ReviewerAddedEvent());
registerClass(new PatchSetCreatedEvent());
registerClass(new TopicChangedEvent());
+ registerClass(new ProjectCreatedEvent());
}
/** Register an event.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ProjectCreatedEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ProjectCreatedEvent.java
new file mode 100644
index 0000000..c1534df
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ProjectCreatedEvent.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2015 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.events;
+
+import com.google.gerrit.reviewdb.client.Project;
+
+public class ProjectCreatedEvent extends ProjectEvent {
+ public String projectName;
+ public String headName;
+
+ public ProjectCreatedEvent() {
+ super("project-created");
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return new Project.NameKey(projectName);
+ }
+
+ public String getHeadName() {
+ return headName;
+ }
+}