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:
@@ -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
|
configuration for this label in child projects will be ignored. Defaults
|
||||||
to true.
|
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]]
|
[[label_example]]
|
||||||
Example
|
Example
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ public class LabelType {
|
|||||||
protected short maxPositive;
|
protected short maxPositive;
|
||||||
|
|
||||||
private transient boolean canOverride;
|
private transient boolean canOverride;
|
||||||
|
private transient List<String> refPatterns;
|
||||||
private transient List<Integer> intList;
|
private transient List<Integer> intList;
|
||||||
private transient Map<Short, LabelValue> byValue;
|
private transient Map<Short, LabelValue> byValue;
|
||||||
|
|
||||||
@@ -157,10 +158,18 @@ public class LabelType {
|
|||||||
return canOverride;
|
return canOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getRefPatterns() {
|
||||||
|
return refPatterns;
|
||||||
|
}
|
||||||
|
|
||||||
public void setCanOverride(boolean canOverride) {
|
public void setCanOverride(boolean canOverride) {
|
||||||
this.canOverride = canOverride;
|
this.canOverride = canOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRefPatterns(List<String> refPatterns) {
|
||||||
|
this.refPatterns = refPatterns;
|
||||||
|
}
|
||||||
|
|
||||||
public List<LabelValue> getValues() {
|
public List<LabelValue> getValues() {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ import java.io.BufferedReader;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
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_COPY_MAX_SCORE = "copyMaxScore";
|
||||||
private static final String KEY_VALUE = "value";
|
private static final String KEY_VALUE = "value";
|
||||||
private static final String KEY_CAN_OVERRIDE = "canOverride";
|
private static final String KEY_CAN_OVERRIDE = "canOverride";
|
||||||
|
private static final String KEY_Branch = "branch";
|
||||||
private static final Set<String> LABEL_FUNCTIONS = ImmutableSet.of(
|
private static final Set<String> LABEL_FUNCTIONS = ImmutableSet.of(
|
||||||
"MaxWithBlock", "AnyWithBlock", "MaxNoBlock", "NoBlock", "NoOp");
|
"MaxWithBlock", "AnyWithBlock", "MaxNoBlock", "NoBlock", "NoOp");
|
||||||
|
|
||||||
@@ -651,10 +653,17 @@ public class ProjectConfig extends VersionedMetaData {
|
|||||||
rc.getBoolean(LABEL, name, KEY_COPY_MAX_SCORE, false));
|
rc.getBoolean(LABEL, name, KEY_COPY_MAX_SCORE, false));
|
||||||
label.setCanOverride(
|
label.setCanOverride(
|
||||||
rc.getBoolean(LABEL, name, KEY_CAN_OVERRIDE, true));
|
rc.getBoolean(LABEL, name, KEY_CAN_OVERRIDE, true));
|
||||||
|
label.setRefPatterns(getStringListOrNull(rc, LABEL, name, KEY_Branch));
|
||||||
labelSections.put(name, label);
|
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) {
|
private void loadCommentLinkSections(Config rc) {
|
||||||
Set<String> subsections = rc.getSubsections(COMMENTLINK);
|
Set<String> subsections = rc.getSubsections(COMMENTLINK);
|
||||||
commentLinkSections = Lists.newArrayListWithCapacity(subsections.size());
|
commentLinkSections = Lists.newArrayListWithCapacity(subsections.size());
|
||||||
|
|||||||
@@ -14,8 +14,11 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.project;
|
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.LabelTypes;
|
||||||
import com.google.gerrit.common.data.PermissionRange;
|
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.SubmitRecord;
|
||||||
import com.google.gerrit.common.data.SubmitTypeRecord;
|
import com.google.gerrit.common.data.SubmitTypeRecord;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
@@ -242,9 +245,28 @@ public class ChangeControl {
|
|||||||
&& getRefControl().canUpload(); // as long as you can upload too
|
&& 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() {
|
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. */
|
/** All value ranges of any allowed label permission. */
|
||||||
@@ -403,6 +425,11 @@ public class ChangeControl {
|
|||||||
return resultsToSubmitRecord(evaluator.getSubmitRule(), results);
|
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,
|
private List<SubmitRecord> cannotSubmitDraft(ReviewDb db, PatchSet patchSet,
|
||||||
ChangeData cd) {
|
ChangeData cd) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ package gerrit;
|
|||||||
|
|
||||||
import com.google.gerrit.common.data.LabelType;
|
import com.google.gerrit.common.data.LabelType;
|
||||||
import com.google.gerrit.common.data.LabelValue;
|
import com.google.gerrit.common.data.LabelValue;
|
||||||
import com.google.gerrit.rules.PrologEnvironment;
|
|
||||||
import com.google.gerrit.rules.StoredValues;
|
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.IntegerTerm;
|
||||||
import com.googlecode.prolog_cafe.lang.ListTerm;
|
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 {
|
public Operation exec(Prolog engine) throws PrologException {
|
||||||
engine.setB0();
|
engine.setB0();
|
||||||
Term a1 = arg1.dereference();
|
Term a1 = arg1.dereference();
|
||||||
|
List<LabelType> list =
|
||||||
PrologEnvironment env = (PrologEnvironment) engine.control;
|
StoredValues.CHANGE_CONTROL.get(engine).getLabelTypes().getLabelTypes();
|
||||||
ProjectState state = env.getArgs().getProjectCache()
|
|
||||||
.get(StoredValues.CHANGE.get(engine).getDest().getParentKey());
|
|
||||||
if (state == null) {
|
|
||||||
return engine.fail();
|
|
||||||
}
|
|
||||||
List<LabelType> list = state.getLabelTypes().getLabelTypes();
|
|
||||||
Term head = Prolog.Nil;
|
Term head = Prolog.Nil;
|
||||||
for (int idx = list.size() - 1; 0 <= idx; idx--) {
|
for (int idx = list.size() - 1; 0 <= idx; idx--) {
|
||||||
head = new ListTerm(export(list.get(idx)), head);
|
head = new ListTerm(export(list.get(idx)), head);
|
||||||
|
|||||||
Reference in New Issue
Block a user