Support branch specific labels

By default a given project's label applicable scope is all changes
on all branches of this project and its child projects.

Label's applicable scope can be branch specific via configuration.
E.g. create a label 'Video-Qualify' on parent project and configure
the 'branch' as:

[label "Video-Qualify"]
    branch = refs/heads/video-1.0/*
    branch = refs/heads/video-1.1/Kino

Then ONLY changes in above branch scope of parent project and child
projects will be affected by 'Video-Qualify'.

Please note the 'branch' is independent from the branch scope related
to access control defined in 'access' parts in 'project.config' file.
That means from the UI a user can always assign permissions for that
label on a branch, but this permission is then ignored if the label
doesn't apply for that branch.

Change-Id: I414e62503b0da4af022abbc9988593ba9ac87916
This commit is contained in:
Bruce Zu
2013-09-06 01:23:03 +08:00
parent 9aaf3d15e3
commit 9b22c3ab2d
5 changed files with 73 additions and 12 deletions

View File

@@ -244,6 +244,30 @@ If false, the label cannot be overridden by child projects. Any
configuration for this label in child projects will be ignored. Defaults
to true.
[[label_branch]]
`label.Label-Name.branch`
~~~~~~~~~~~~~~~~~~~~~~~~~
By default a given project's label applicable scope is all changes
on all branches of this project and its child projects.
Label's applicable scope can be branch specific via configuration.
E.g. create a label `Video-Qualify` on parent project and configure
the `branch` as:
====
[label "Video-Qualify"]
branch = refs/heads/video-1.0/*
branch = refs/heads/video-1.1/Kino
====
Then *only* changes in above branch scope of parent project and child
projects will be affected by `Video-Qualify`.
NOTE: The `branch` is independent from the branch scope defined in `access`
parts in `project.config` file. That means from the UI a user can always
assign permissions for that label on a branch, but this permission is then
ignored if the label doesn't apply for that branch.
[[label_example]]
Example

View File

@@ -103,6 +103,7 @@ public class LabelType {
protected short maxPositive;
private transient boolean canOverride;
private transient List<String> refPatterns;
private transient List<Integer> intList;
private transient Map<Short, LabelValue> byValue;
@@ -157,10 +158,18 @@ public class LabelType {
return canOverride;
}
public List<String> getRefPatterns() {
return refPatterns;
}
public void setCanOverride(boolean canOverride) {
this.canOverride = canOverride;
}
public void setRefPatterns(List<String> refPatterns) {
this.refPatterns = refPatterns;
}
public List<LabelValue> getValues() {
return values;
}

View File

@@ -59,6 +59,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
@@ -127,6 +128,7 @@ public class ProjectConfig extends VersionedMetaData {
private static final String KEY_COPY_MAX_SCORE = "copyMaxScore";
private static final String KEY_VALUE = "value";
private static final String KEY_CAN_OVERRIDE = "canOverride";
private static final String KEY_Branch = "branch";
private static final Set<String> LABEL_FUNCTIONS = ImmutableSet.of(
"MaxWithBlock", "AnyWithBlock", "MaxNoBlock", "NoBlock", "NoOp");
@@ -651,10 +653,17 @@ public class ProjectConfig extends VersionedMetaData {
rc.getBoolean(LABEL, name, KEY_COPY_MAX_SCORE, false));
label.setCanOverride(
rc.getBoolean(LABEL, name, KEY_CAN_OVERRIDE, true));
label.setRefPatterns(getStringListOrNull(rc, LABEL, name, KEY_Branch));
labelSections.put(name, label);
}
}
private List<String> getStringListOrNull(Config rc, String section,
String subSection, String name) {
String[] ac = rc.getStringList(section, subSection, name);
return ac.length == 0 ? null : Arrays.asList(ac);
}
private void loadCommentLinkSections(Config rc) {
Set<String> subsections = rc.getSubsections(COMMENTLINK);
commentLinkSections = Lists.newArrayListWithCapacity(subsections.size());

View File

@@ -14,8 +14,11 @@
package com.google.gerrit.server.project;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.RefConfigSection;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.reviewdb.client.Account;
@@ -242,9 +245,28 @@ public class ChangeControl {
&& getRefControl().canUpload(); // as long as you can upload too
}
/** All available label types for this project. */
/** All available label types for this change. */
public LabelTypes getLabelTypes() {
return getProjectControl().getLabelTypes();
String destBranch = getChange().getDest().get();
List<LabelType> all = getProjectControl().getLabelTypes().getLabelTypes();
List<LabelType> r = Lists.newArrayListWithCapacity(all.size());
for (LabelType l : all) {
List<String> refs = l.getRefPatterns();
if (refs == null) {
r.add(l);
} else {
for (String refPattern : refs) {
if (RefConfigSection.isValid(refPattern)
&& match(destBranch, refPattern)) {
r.add(l);
break;
}
}
}
}
return new LabelTypes(r);
}
/** All value ranges of any allowed label permission. */
@@ -403,6 +425,11 @@ public class ChangeControl {
return resultsToSubmitRecord(evaluator.getSubmitRule(), results);
}
private boolean match(String destBranch, String refPattern) {
return RefPatternMatcher.getMatcher(refPattern).match(destBranch,
this.getRefControl().getCurrentUser().getUserName());
}
private List<SubmitRecord> cannotSubmitDraft(ReviewDb db, PatchSet patchSet,
ChangeData cd) {
try {

View File

@@ -16,9 +16,7 @@ package gerrit;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.rules.PrologEnvironment;
import com.google.gerrit.rules.StoredValues;
import com.google.gerrit.server.project.ProjectState;
import com.googlecode.prolog_cafe.lang.IntegerTerm;
import com.googlecode.prolog_cafe.lang.ListTerm;
@@ -55,14 +53,8 @@ class PRED_get_legacy_label_types_1 extends Predicate.P1 {
public Operation exec(Prolog engine) throws PrologException {
engine.setB0();
Term a1 = arg1.dereference();
PrologEnvironment env = (PrologEnvironment) engine.control;
ProjectState state = env.getArgs().getProjectCache()
.get(StoredValues.CHANGE.get(engine).getDest().getParentKey());
if (state == null) {
return engine.fail();
}
List<LabelType> list = state.getLabelTypes().getLabelTypes();
List<LabelType> list =
StoredValues.CHANGE_CONTROL.get(engine).getLabelTypes().getLabelTypes();
Term head = Prolog.Nil;
for (int idx = list.size() - 1; 0 <= idx; idx--) {
head = new ListTerm(export(list.get(idx)), head);