Merge "Introduce ref operation validation"
This commit is contained in:
@@ -21,6 +21,17 @@ and cherry-pick buttons.
|
||||
Out of the box, Gerrit includes a plugin that checks the length of the
|
||||
subject and body lines of commit messages on uploaded commits.
|
||||
|
||||
[[ref-operation-validation]]
|
||||
== Ref operation validation
|
||||
|
||||
|
||||
Plugins implementing the `RefOperationValidationListener` interface can
|
||||
perform additional validation checks against ref creation/deletion operation
|
||||
before it is applied to the git repository.
|
||||
|
||||
If the ref operation fails the validation, the plugin can throw an exception
|
||||
which will cause the operation to fail.
|
||||
|
||||
[[pre-merge-validation]]
|
||||
== Pre-merge validation
|
||||
|
||||
|
@@ -89,6 +89,8 @@ import com.google.gerrit.server.git.validators.CommitValidators;
|
||||
import com.google.gerrit.server.git.validators.MergeValidationListener;
|
||||
import com.google.gerrit.server.git.validators.MergeValidators;
|
||||
import com.google.gerrit.server.git.validators.MergeValidators.ProjectConfigValidator;
|
||||
import com.google.gerrit.server.git.validators.RefOperationValidationListener;
|
||||
import com.google.gerrit.server.git.validators.RefOperationValidators;
|
||||
import com.google.gerrit.server.git.validators.UploadValidationListener;
|
||||
import com.google.gerrit.server.git.validators.UploadValidators;
|
||||
import com.google.gerrit.server.group.GroupModule;
|
||||
@@ -269,6 +271,7 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
.to(ProjectConfigEntry.UpdateChecker.class);
|
||||
DynamicSet.setOf(binder(), ChangeListener.class);
|
||||
DynamicSet.setOf(binder(), CommitValidationListener.class);
|
||||
DynamicSet.setOf(binder(), RefOperationValidationListener.class);
|
||||
DynamicSet.setOf(binder(), MergeValidationListener.class);
|
||||
DynamicSet.setOf(binder(), ProjectCreationValidationListener.class);
|
||||
DynamicSet.setOf(binder(), GroupCreationValidationListener.class);
|
||||
@@ -291,6 +294,7 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
bind(AnonymousUser.class);
|
||||
|
||||
factory(CommitValidators.Factory.class);
|
||||
factory(RefOperationValidators.Factory.class);
|
||||
factory(MergeValidators.Factory.class);
|
||||
factory(ProjectConfigValidator.Factory.class);
|
||||
factory(NotesBranchUtil.Factory.class);
|
||||
|
@@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2014 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;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
|
||||
import org.eclipse.jgit.transport.ReceiveCommand;
|
||||
|
||||
public class RefOperationReceivedEvent extends ChangeEvent {
|
||||
public final String type = "ref-received";
|
||||
public ReceiveCommand command;
|
||||
public Project project;
|
||||
public IdentifiedUser user;
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2014 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.git.validators;
|
||||
|
||||
import com.google.gerrit.server.validators.ValidationException;
|
||||
|
||||
public class RefOperationValidationException extends ValidationException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Iterable<ValidationMessage> messages;
|
||||
|
||||
public RefOperationValidationException(String reason,
|
||||
Iterable<ValidationMessage> messages) {
|
||||
super(reason);
|
||||
this.messages = messages;
|
||||
}
|
||||
|
||||
public Iterable<ValidationMessage> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
StringBuilder msg = new StringBuilder(super.getMessage());
|
||||
for (ValidationMessage error : messages) {
|
||||
msg.append("\n").append(error.getMessage());
|
||||
}
|
||||
return msg.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2014 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.git.validators;
|
||||
|
||||
import com.google.gerrit.extensions.annotations.ExtensionPoint;
|
||||
import com.google.gerrit.server.events.RefOperationReceivedEvent;
|
||||
import com.google.gerrit.server.validators.ValidationException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Listener to provide validation on operation that is going to be performed on
|
||||
* given ref
|
||||
*/
|
||||
@ExtensionPoint
|
||||
public interface RefOperationValidationListener {
|
||||
/**
|
||||
* Validate a ref operation before it is performed.
|
||||
*
|
||||
* @param refEvent ref operation specification
|
||||
* @return empty list or informational messages on success
|
||||
* @throws ValidationException if the ref operation fails to validate
|
||||
*/
|
||||
List<ValidationMessage> onRefOperation(RefOperationReceivedEvent refEvent)
|
||||
throws ValidationException;
|
||||
}
|
@@ -0,0 +1,100 @@
|
||||
// Copyright (C) 2014 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.git.validators;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.events.RefOperationReceivedEvent;
|
||||
import com.google.gerrit.server.validators.ValidationException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import org.eclipse.jgit.lib.RefUpdate;
|
||||
import org.eclipse.jgit.transport.ReceiveCommand;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RefOperationValidators {
|
||||
private static final GetErrorMessages GET_ERRORS = new GetErrorMessages();
|
||||
private static final Logger LOG = LoggerFactory
|
||||
.getLogger(RefOperationValidators.class);
|
||||
|
||||
public interface Factory {
|
||||
RefOperationValidators create(Project project, IdentifiedUser user, ReceiveCommand cmd);
|
||||
}
|
||||
|
||||
public static ReceiveCommand getCommand(RefUpdate update, ReceiveCommand.Type type) {
|
||||
return new ReceiveCommand(update.getOldObjectId(), update.getNewObjectId(),
|
||||
update.getName(), type);
|
||||
}
|
||||
|
||||
private final RefOperationReceivedEvent event;
|
||||
private final DynamicSet<RefOperationValidationListener> refOperationValidationListeners;
|
||||
|
||||
@Inject
|
||||
RefOperationValidators(
|
||||
DynamicSet<RefOperationValidationListener> refOperationValidationListeners,
|
||||
@Assisted Project project, @Assisted IdentifiedUser user,
|
||||
@Assisted ReceiveCommand cmd) {
|
||||
this.refOperationValidationListeners = refOperationValidationListeners;
|
||||
event = new RefOperationReceivedEvent();
|
||||
event.command = cmd;
|
||||
event.project = project;
|
||||
event.user = user;
|
||||
}
|
||||
|
||||
public List<ValidationMessage> validateForRefOperation()
|
||||
throws RefOperationValidationException {
|
||||
|
||||
List<ValidationMessage> messages = Lists.newArrayList();
|
||||
boolean withException = false;
|
||||
try {
|
||||
for (RefOperationValidationListener listener : refOperationValidationListeners) {
|
||||
messages.addAll(listener.onRefOperation(event));
|
||||
}
|
||||
} catch (ValidationException e) {
|
||||
messages.add(new ValidationMessage(e.getMessage(), true));
|
||||
withException = true;
|
||||
}
|
||||
|
||||
if (withException) {
|
||||
throwException(messages, event);
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
private void throwException(Iterable<ValidationMessage> messages,
|
||||
RefOperationReceivedEvent event) throws RefOperationValidationException {
|
||||
Iterable<ValidationMessage> errors = Iterables.filter(messages, GET_ERRORS);
|
||||
String header = String.format(
|
||||
"Ref \"%s\" %S in project %s validation failed", event.command.getRefName(),
|
||||
event.command.getType(), event.project.getName());
|
||||
LOG.error(header);
|
||||
throw new RefOperationValidationException(header, errors);
|
||||
}
|
||||
|
||||
private static class GetErrorMessages implements Predicate<ValidationMessage> {
|
||||
@Override
|
||||
public boolean apply(ValidationMessage input) {
|
||||
return input.isError();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user