Permit booleans in project.config to be inherited

Allow the parent project to supply the value for properties:

  * use content merge
  * require contributor agreement
  * require change id
  * require signed off by

This makes it easier to configure a large number of projects, as
the properties can be defined once on the parent and inherited.

This commit modifies the default behavior when the variable is not
defined in project.config, it now inherits from the parent rather
than being false.

Change-Id: Ic4a6ea6ff23ad16f1fc82b70cc89a480f59db010
This commit is contained in:
Shawn O. Pearce 2012-10-17 18:20:40 -07:00
parent 8b5154fbac
commit 2462ccb8df
13 changed files with 302 additions and 171 deletions

View File

@ -11,11 +11,11 @@ SYNOPSIS
'ssh' -p <port> <host> 'gerrit set-project'
[--description <DESC> | -d <DESC>]
[--submit-type <TYPE> | -t <TYPE>]
[--use|no-contributor-agreements | --ca|nca]
[--use|no-signed-off-by | --so|nso]
[--use|no-content-merge]
[--require|no-change-id | --id|nid]
[--project-state | --ps]
[--contributor-agreements <true|false|inherit>]
[--signed-off-by <true|false|inherit>]
[--content-merge <true|false|inherit>]
[--change-id <true|false|inherit>]
[--project-state <STATE> | --ps <STATE>]
<NAME>
DESCRIPTION
@ -63,26 +63,23 @@ Description values containing spaces should be quoted in single quotes
For more details see
link:project-setup.html#submit_type[Change Submit Actions].
--use|no-content-merge::
--content-merge::
If enabled, Gerrit will try to perform a 3-way merge of text
file content when a file has been modified by both the
destination branch and the change being submitted. This
option only takes effect if submit type is not
FAST_FORWARD_ONLY.
--use|no-contributor-agreements::
--ca|nca::
--contributor-agreements::
If enabled, authors must complete a contributor agreement
on the site before pushing any commits or changes to this
project.
--use|no-signed-off-by::
--so|nso:
--signed-off-by::
If enabled, each change must contain a Signed-off-by line
from either the author or the uploader in the commit message.
--require|no-change-id::
--id|nid::
--change-id::
Require a valid link:user-changeid.html[Change-Id] footer
in any commit uploaded for review. This does not apply to
commits pushed directly to a branch or tag.
@ -103,7 +100,7 @@ and use 'merge if necessary' as merge strategy:
====
$ ssh -p 29418 review.example.com gerrit set-project example --submit-type MERGE_IF_NECESSARY\
--require-change-id --no-content-merge --project-state HIDDEN
--change-id true --content-merge false --project-state HIDDEN
====
GERRIT

View File

@ -21,13 +21,16 @@ import com.google.gerrit.client.ui.OnEditEnabler;
import com.google.gerrit.client.ui.SmallHeading;
import com.google.gerrit.common.data.ProjectDetail;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.Project.SubmitType;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.VerticalPanel;
@ -37,14 +40,14 @@ public class ProjectInfoScreen extends ProjectScreen {
private Project project;
private Panel projectOptionsPanel;
private CheckBox requireChangeID;
private ListBox requireChangeID;
private ListBox submitType;
private ListBox state;
private CheckBox useContentMerge;
private ListBox contentMerge;
private Panel agreementsPanel;
private CheckBox useContributorAgreements;
private CheckBox useSignedOffBy;
private ListBox contributorAgreements;
private ListBox signedOffBy;
private NpTextArea descTxt;
private Button saveProject;
@ -96,10 +99,10 @@ public class ProjectInfoScreen extends ProjectScreen {
final boolean canModifyState) {
submitType.setEnabled(canModifyMergeType);
state.setEnabled(canModifyState);
useContentMerge.setEnabled(canModifyMergeType);
contentMerge.setEnabled(canModifyMergeType);
descTxt.setEnabled(canModifyDescription);
useContributorAgreements.setEnabled(canModifyAgreements);
useSignedOffBy.setEnabled(canModifyAgreements);
contributorAgreements.setEnabled(canModifyAgreements);
signedOffBy.setEnabled(canModifyAgreements);
requireChangeID.setEnabled(canModifyMergeType);
}
@ -142,19 +145,33 @@ public class ProjectInfoScreen extends ProjectScreen {
saveEnabler.listenTo(state);
projectOptionsPanel.add(state);
useContentMerge = new CheckBox(Util.C.useContentMerge(), true);
saveEnabler.listenTo(useContentMerge);
projectOptionsPanel.add(useContentMerge);
contentMerge = newInheritedBooleanBox();
FlowPanel fp = new FlowPanel();
fp.add(contentMerge);
fp.add(new InlineLabel(Util.C.useContentMerge()));
saveEnabler.listenTo(contentMerge);
projectOptionsPanel.add(fp);
requireChangeID = new CheckBox(Util.C.requireChangeID(), true);
requireChangeID = newInheritedBooleanBox();
fp = new FlowPanel();
fp.add(requireChangeID);
fp.add(new InlineHTML(Util.C.requireChangeID()));
saveEnabler.listenTo(requireChangeID);
projectOptionsPanel.add(requireChangeID);
projectOptionsPanel.add(fp);
add(projectOptionsPanel);
}
private static ListBox newInheritedBooleanBox() {
ListBox box = new ListBox();
for (InheritedBoolean b : InheritedBoolean.values()) {
box.addItem(b.name(), b.name());
}
return box;
}
/**
* Enables the {@link #useContentMerge} checkbox if the selected submit type
* Enables the {@link #contentMerge} checkbox if the selected submit type
* allows the usage of content merge.
* If the submit type (currently only 'Fast Forward Only') does not allow
* content merge the useContentMerge checkbox gets disabled.
@ -162,10 +179,10 @@ public class ProjectInfoScreen extends ProjectScreen {
private void setEnabledForUseContentMerge() {
if (SubmitType.FAST_FORWARD_ONLY.equals(Project.SubmitType
.valueOf(submitType.getValue(submitType.getSelectedIndex())))) {
useContentMerge.setEnabled(false);
useContentMerge.setValue(false);
contentMerge.setEnabled(false);
setBool(contentMerge, InheritedBoolean.FALSE);
} else {
useContentMerge.setEnabled(submitType.isEnabled());
contentMerge.setEnabled(submitType.isEnabled());
}
}
@ -173,13 +190,21 @@ public class ProjectInfoScreen extends ProjectScreen {
agreementsPanel = new VerticalPanel();
agreementsPanel.add(new SmallHeading(Util.C.headingAgreements()));
useContributorAgreements = new CheckBox(Util.C.useContributorAgreements());
saveEnabler.listenTo(useContributorAgreements);
agreementsPanel.add(useContributorAgreements);
contributorAgreements = newInheritedBooleanBox();
if (Gerrit.getConfig().isUseContributorAgreements()) {
FlowPanel fp = new FlowPanel();
fp.add(contributorAgreements);
fp.add(new InlineLabel(Util.C.useContributorAgreements()));
saveEnabler.listenTo(contributorAgreements);
agreementsPanel.add(fp);
}
useSignedOffBy = new CheckBox(Util.C.useSignedOffBy(), true);
saveEnabler.listenTo(useSignedOffBy);
agreementsPanel.add(useSignedOffBy);
signedOffBy = newInheritedBooleanBox();
FlowPanel fp = new FlowPanel();
fp.add(signedOffBy);
fp.add(new InlineHTML(Util.C.useSignedOffBy()));
saveEnabler.listenTo(signedOffBy);
agreementsPanel.add(fp);
add(agreementsPanel);
}
@ -209,21 +234,31 @@ public class ProjectInfoScreen extends ProjectScreen {
}
}
private static void setBool(ListBox box, InheritedBoolean val) {
for (int i = 0; i < box.getItemCount(); i++) {
if (val.name().equals(box.getValue(i))) {
box.setSelectedIndex(i);
break;
}
}
}
private static InheritedBoolean getBool(ListBox box) {
int i = box.getSelectedIndex();
if (i >= 0) {
return InheritedBoolean.valueOf(box.getValue(i));
}
return InheritedBoolean.INHERIT;
}
void display(final ProjectDetail result) {
project = result.project;
final boolean isall =
Gerrit.getConfig().getWildProject().equals(project.getNameKey());
projectOptionsPanel.setVisible(!isall);
agreementsPanel.setVisible(!isall);
useContributorAgreements.setVisible(Gerrit.getConfig()
.isUseContributorAgreements());
descTxt.setText(project.getDescription());
useContributorAgreements.setValue(project.isUseContributorAgreements());
useSignedOffBy.setValue(project.isUseSignedOffBy());
useContentMerge.setValue(project.isUseContentMerge());
requireChangeID.setValue(project.isRequireChangeID());
setBool(contributorAgreements, project.getUseContributorAgreements());
setBool(signedOffBy, project.getUseSignedOffBy());
setBool(contentMerge, project.getUseContentMerge());
setBool(requireChangeID, project.getRequireChangeID());
setSubmitType(project.getSubmitType());
setState(project.getState());
@ -232,10 +267,10 @@ public class ProjectInfoScreen extends ProjectScreen {
private void doSave() {
project.setDescription(descTxt.getText().trim());
project.setUseContributorAgreements(useContributorAgreements.getValue());
project.setUseSignedOffBy(useSignedOffBy.getValue());
project.setUseContentMerge(useContentMerge.getValue());
project.setRequireChangeID(requireChangeID.getValue());
project.setUseContributorAgreements(getBool(contributorAgreements));
project.setUseSignedOffBy(getBool(signedOffBy));
project.setUseContentMerge(getBool(contentMerge));
project.setRequireChangeID(getBool(requireChangeID));
if (submitType.getSelectedIndex() >= 0) {
project.setSubmitType(Project.SubmitType.valueOf(submitType
.getValue(submitType.getSelectedIndex())));

View File

@ -85,13 +85,19 @@ public final class Project {
HIDDEN;
}
public static enum InheritedBoolean {
TRUE,
FALSE,
INHERIT;
}
protected NameKey name;
protected String description;
protected boolean useContributorAgreements;
protected InheritedBoolean useContributorAgreements;
protected boolean useSignedOffBy;
protected InheritedBoolean useSignedOffBy;
protected SubmitType submitType;
@ -99,9 +105,9 @@ public final class Project {
protected NameKey parent;
protected boolean requireChangeID;
protected InheritedBoolean requireChangeID;
protected boolean useContentMerge;
protected InheritedBoolean useContentMerge;
protected Project() {
}
@ -110,6 +116,10 @@ public final class Project {
name = nameKey;
submitType = SubmitType.MERGE_IF_NECESSARY;
state = State.ACTIVE;
useContributorAgreements = InheritedBoolean.INHERIT;
useSignedOffBy = InheritedBoolean.INHERIT;
requireChangeID = InheritedBoolean.INHERIT;
useContentMerge = InheritedBoolean.INHERIT;
}
public Project.NameKey getNameKey() {
@ -128,35 +138,35 @@ public final class Project {
description = d;
}
public boolean isUseContributorAgreements() {
public InheritedBoolean getUseContributorAgreements() {
return useContributorAgreements;
}
public void setUseContributorAgreements(final boolean u) {
useContributorAgreements = u;
}
public boolean isUseSignedOffBy() {
public InheritedBoolean getUseSignedOffBy() {
return useSignedOffBy;
}
public boolean isUseContentMerge() {
public InheritedBoolean getUseContentMerge() {
return useContentMerge;
}
public boolean isRequireChangeID() {
public InheritedBoolean getRequireChangeID() {
return requireChangeID;
}
public void setUseSignedOffBy(final boolean sbo) {
public void setUseContributorAgreements(final InheritedBoolean u) {
useContributorAgreements = u;
}
public void setUseSignedOffBy(final InheritedBoolean sbo) {
useSignedOffBy = sbo;
}
public void setUseContentMerge(final boolean cm) {
public void setUseContentMerge(final InheritedBoolean cm) {
useContentMerge = cm;
}
public void setRequireChangeID(final boolean cid) {
public void setRequireChangeID(final InheritedBoolean cid) {
requireChangeID = cid;
}

View File

@ -132,7 +132,7 @@ public class MergeOp {
private final PersonIdent myIdent;
private final Branch.NameKey destBranch;
private Project destProject;
private ProjectState destProject;
private final ListMultimap<SubmitType, CodeReviewCommit> toMerge;
private final List<CodeReviewCommit> potentiallyStillSubmittable;
private final Map<Change.Id, CodeReviewCommit> commits;
@ -253,11 +253,10 @@ public class MergeOp {
}
private void setDestProject() throws MergeException {
final ProjectState pe = projectCache.get(destBranch.getParentKey());
if (pe == null) {
destProject = projectCache.get(destBranch.getParentKey());
if (destProject == null) {
throw new MergeException("No such project: " + destBranch.getParentKey());
}
destProject = pe.getProject();
}
private void openSchema() throws OrmException {
@ -602,12 +601,13 @@ public class MergeOp {
if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) {
if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) {
try {
ProjectConfig cfg = new ProjectConfig(destProject.getNameKey());
ProjectConfig cfg =
new ProjectConfig(destProject.getProject().getNameKey());
cfg.load(repo, mergeTip);
} catch (Exception e) {
throw new MergeException("Submit would store invalid"
+ " project configuration " + mergeTip.name() + " for "
+ destProject.getName(), e);
+ destProject.getProject().getName(), e);
}
}
@ -627,10 +627,11 @@ public class MergeOp {
}
if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) {
projectCache.evict(destProject);
ProjectState ps = projectCache.get(destProject.getNameKey());
repoManager.setProjectDescription(destProject.getNameKey(), //
ps.getProject().getDescription());
projectCache.evict(destProject.getProject());
destProject = projectCache.get(destProject.getProject().getNameKey());
repoManager.setProjectDescription(
destProject.getProject().getNameKey(),
destProject.getProject().getDescription());
}
replication.fire(destBranch.getParentKey(), branchUpdate.getName());
@ -732,8 +733,8 @@ public class MergeOp {
private void updateSubscriptions(final List<Change> submitted) {
if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) {
SubmoduleOp subOp =
subOpFactory.create(destBranch, mergeTip, rw, repo, destProject,
submitted, commits);
subOpFactory.create(destBranch, mergeTip, rw, repo,
destProject.getProject(), submitted, commits);
try {
subOp.update();
} catch (SubmoduleException e) {

View File

@ -282,12 +282,12 @@ public class ProjectConfig extends VersionedMetaData {
}
p.setParentName(rc.getString(ACCESS, null, KEY_INHERIT_FROM));
p.setUseContributorAgreements(getBoolean(rc, RECEIVE, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, false));
p.setUseSignedOffBy(getBoolean(rc, RECEIVE, KEY_REQUIRE_SIGNED_OFF_BY, false));
p.setRequireChangeID(getBoolean(rc, RECEIVE, KEY_REQUIRE_CHANGE_ID, false));
p.setUseContributorAgreements(getEnum(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, Project.InheritedBoolean.INHERIT));
p.setUseSignedOffBy(getEnum(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, Project.InheritedBoolean.INHERIT));
p.setRequireChangeID(getEnum(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, Project.InheritedBoolean.INHERIT));
p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, defaultSubmitAction));
p.setUseContentMerge(getBoolean(rc, SUBMIT, KEY_MERGE_CONTENT, false));
p.setUseContentMerge(getEnum(rc, SUBMIT, null, KEY_MERGE_CONTENT, Project.InheritedBoolean.INHERIT));
p.setState(getEnum(rc, PROJECT, null, KEY_STATE, defaultStateValue));
loadAccountsSection(rc, groupsByName);
@ -525,12 +525,12 @@ public class ProjectConfig extends VersionedMetaData {
}
set(rc, ACCESS, null, KEY_INHERIT_FROM, p.getParentName());
set(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, p.isUseContributorAgreements());
set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, p.isUseSignedOffBy());
set(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, p.isRequireChangeID());
set(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, p.getUseContributorAgreements(), Project.InheritedBoolean.INHERIT);
set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, p.getUseSignedOffBy(), Project.InheritedBoolean.INHERIT);
set(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, p.getRequireChangeID(), Project.InheritedBoolean.INHERIT);
set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);
set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.isUseContentMerge());
set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.getUseContentMerge(), Project.InheritedBoolean.INHERIT);
set(rc, PROJECT, null, KEY_STATE, p.getState(), null);
@ -734,16 +734,6 @@ public class ProjectConfig extends VersionedMetaData {
saveUTF8(GROUP_LIST, buf.toString());
}
private boolean getBoolean(Config rc, String section, String name,
boolean defaultValue) {
try {
return rc.getBoolean(section, name, defaultValue);
} catch (IllegalArgumentException err) {
error(new ValidationError(PROJECT_CONFIG, err.getMessage()));
return defaultValue;
}
}
private <E extends Enum<?>> E getEnum(Config rc, String section,
String subsection, String name, E defaultValue) {
try {

View File

@ -14,8 +14,8 @@
package com.google.gerrit.server.git;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT;
@ -79,6 +79,7 @@ import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.jcraft.jsch.HostKey;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
@ -1868,7 +1869,7 @@ public class ReceiveCommits {
&& ctl.canForgeCommitter()
&& ctl.canForgeGerritServerIdentity()
&& ctl.canUploadMerges()
&& !project.isUseSignedOffBy()
&& !projectControl.getProjectState().isUseSignedOffBy()
&& Iterables.isEmpty(rejectCommits)
&& !GitRepositoryManager.REF_CONFIG.equals(ctl.getRefName())
&& !(MagicBranch.isMagicBranch(cmd.getRefName())
@ -1954,7 +1955,7 @@ public class ReceiveCommits {
return false;
}
if (project.isUseSignedOffBy()) {
if (projectControl.getProjectState().isUseSignedOffBy()) {
// If the project wants Signed-off-by / Acked-by lines, verify we
// have them for the blamable parties involved on this change.
//
@ -1978,7 +1979,7 @@ public class ReceiveCommits {
final List<String> idList = c.getFooterLines(CHANGE_ID);
if (MagicBranch.isMagicBranch(cmd.getRefName()) || NEW_PATCHSET.matcher(cmd.getRefName()).matches()) {
if (idList.isEmpty()) {
if (project.isRequireChangeID()) {
if (projectControl.getProjectState().isRequireChangeID()) {
String errMsg = "missing Change-Id in commit message";
reject(cmd, errMsg);
addMessage(getFixedCommitMsgWithChangeId(errMsg, c));

View File

@ -16,6 +16,7 @@ package com.google.gerrit.server.project;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.Project.SubmitType;
import java.util.List;
@ -27,12 +28,12 @@ public class CreateProjectArgs {
public ProjectControl newParent;
public String projectDescription;
public SubmitType submitType;
public boolean contributorAgreements;
public boolean signedOffBy;
public InheritedBoolean contributorAgreements;
public InheritedBoolean signedOffBy;
public boolean permissionsOnly;
public List<String> branch;
public boolean contentMerge;
public boolean changeIdRequired;
public InheritedBoolean contentMerge;
public InheritedBoolean changeIdRequired;
public boolean createEmptyCommit;
public CreateProjectArgs() {

View File

@ -232,8 +232,7 @@ public class ProjectControl {
String pName = state.getProject().getName();
return new Capable("Upload denied for project '" + pName + "'");
}
Project project = state.getProject();
if (project.isUseContributorAgreements()) {
if (state.isUseContributorAgreements()) {
return verifyActiveContributorAgreement();
}
return Capable.OK;

View File

@ -14,13 +14,16 @@
package com.google.gerrit.server.project;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.rules.PrologEnvironment;
import com.google.gerrit.rules.RulesCache;
import com.google.gerrit.server.CurrentUser;
@ -295,4 +298,63 @@ public class ProjectState {
public boolean isAllProjects() {
return isAllProjects;
}
public boolean isUseContributorAgreements() {
return getInheritedBoolean(new Function<Project, InheritedBoolean>() {
@Override
public InheritedBoolean apply(Project input) {
return input.getUseContributorAgreements();
}
});
}
public boolean isUseContentMerge() {
return getInheritedBoolean(new Function<Project, InheritedBoolean>() {
@Override
public InheritedBoolean apply(Project input) {
return input.getUseContentMerge();
}
});
}
public boolean isUseSignedOffBy() {
return getInheritedBoolean(new Function<Project, InheritedBoolean>() {
@Override
public InheritedBoolean apply(Project input) {
return input.getUseSignedOffBy();
}
});
}
public boolean isRequireChangeID() {
return getInheritedBoolean(new Function<Project, InheritedBoolean>() {
@Override
public InheritedBoolean apply(Project input) {
return input.getRequireChangeID();
}
});
}
private boolean getInheritedBoolean(Function<Project, InheritedBoolean> func) {
Set<Project.NameKey> seen = Sets.newHashSet();
seen.add(getProject().getNameKey());
ProjectState s = this;
do {
switch (func.apply(s.getProject())) {
case TRUE:
return true;
case FALSE:
return false;
case INHERIT:
default:
Project.NameKey parent = s.getProject().getParent(allProjectsName);
if (parent != null && seen.add(parent)) {
s = projectCache.get(parent);
} else {
s = null;
}
}
} while (s != null);
return false;
}
}

View File

@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.ApprovalCategory;
import com.google.gerrit.reviewdb.client.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
@ -209,7 +210,10 @@ public class SchemaCreator {
ProjectConfig config = ProjectConfig.read(md);
Project p = config.getProject();
p.setDescription("Rights inherited by all other projects");
p.setUseContributorAgreements(false);
p.setRequireChangeID(InheritedBoolean.TRUE);
p.setUseContentMerge(InheritedBoolean.FALSE);
p.setUseContributorAgreements(InheritedBoolean.FALSE);
p.setUseSignedOffBy(InheritedBoolean.FALSE);
AccessSection cap = config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true);
AccessSection all = config.getAccessSection(AccessSection.ALL, true);

View File

@ -33,6 +33,7 @@ import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.ApprovalCategory;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
@ -200,8 +201,7 @@ class Schema_53 extends SchemaVersion {
private void loadProject(ResultSet rs, Project project) throws SQLException,
OrmException {
project.setDescription(rs.getString("description"));
project.setUseContributorAgreements("Y".equals(rs
.getString("use_contributor_agreements")));
project.setUseContributorAgreements(asInheritedBoolean(rs, "use_contributor_agreements"));
switch (rs.getString("submit_type").charAt(0)) {
case 'F':
@ -221,12 +221,19 @@ class Schema_53 extends SchemaVersion {
+ rs.getString("submit_type") + " on project " + project.getName());
}
project.setUseSignedOffBy("Y".equals(rs.getString("use_signed_off_by")));
project.setRequireChangeID("Y".equals(rs.getString("require_change_id")));
project.setUseContentMerge("Y".equals(rs.getString("use_content_merge")));
project.setUseSignedOffBy(asInheritedBoolean(rs, "use_signed_off_by"));
project.setRequireChangeID(asInheritedBoolean(rs, "require_change_id"));
project.setUseContentMerge(asInheritedBoolean(rs, "use_content_merge"));
project.setParentName(rs.getString("parent_name"));
}
private static InheritedBoolean asInheritedBoolean(ResultSet rs, String col)
throws SQLException {
return "Y".equals(rs.getString(col))
? Project.InheritedBoolean.TRUE
: Project.InheritedBoolean.INHERIT;
}
private void readOldRefRights(ReviewDb db) throws SQLException {
rightsByProject = new HashMap<Project.NameKey, List<OldRefRight>>();

View File

@ -19,6 +19,7 @@ import com.google.gerrit.common.errors.ProjectCreationFailedException;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.Project.SubmitType;
import com.google.gerrit.server.project.CreateProject;
import com.google.gerrit.server.project.CreateProjectArgs;
@ -64,17 +65,37 @@ final class CreateProjectCommand extends SshCommand {
+ "(default: MERGE_IF_NECESSARY)")
private SubmitType submitType = SubmitType.MERGE_IF_NECESSARY;
@Option(name = "--contributor-agreements", usage = "if contributor agreement is required")
private InheritedBoolean contributorAgreements;
@Option(name = "--signed-off-by", usage = "if signed-off-by is required")
private InheritedBoolean signedOffBy = InheritedBoolean.INHERIT;
@Option(name = "--content-merge", usage = "allow automatic conflict resolving within files")
private InheritedBoolean contentMerge = InheritedBoolean.INHERIT;
@Option(name = "--change-id", usage = "if change-id is required")
private InheritedBoolean requireChangeID = InheritedBoolean.INHERIT;
@Option(name = "--use-contributor-agreements", aliases = {"--ca"}, usage = "if contributor agreement is required")
private boolean contributorAgreements;
void setUseContributorArgreements(boolean on) {
contributorAgreements = InheritedBoolean.TRUE;
}
@Option(name = "--use-signed-off-by", aliases = {"--so"}, usage = "if signed-off-by is required")
private boolean signedOffBy;
void setUseSignedOffBy(boolean on) {
signedOffBy = InheritedBoolean.TRUE;
}
@Option(name = "--use-content-merge", usage = "allow automatic conflict resolving within files")
private boolean contentMerge;
void setUseContentMerge(boolean on) {
contentMerge = InheritedBoolean.TRUE;
}
@Option(name = "--require-change-id", aliases = {"--id"}, usage = "if change-id is required")
private boolean requireChangeID;
void setRequireChangeId(boolean on) {
requireChangeID = InheritedBoolean.TRUE;
}
@Option(name = "--branch", aliases = {"-b"}, metaVar = "BRANCH", usage = "initial branch name\n"
+ "(default: master)")

View File

@ -17,6 +17,7 @@ package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.InheritedBoolean;
import com.google.gerrit.reviewdb.client.Project.State;
import com.google.gerrit.reviewdb.client.Project.SubmitType;
import com.google.gerrit.server.git.MetaDataUpdate;
@ -50,29 +51,57 @@ final class SetProjectCommand extends SshCommand {
+ "(default: MERGE_IF_NECESSARY)")
private SubmitType submitType;
@Option(name = "--contributor-agreements", usage = "if contributor agreement is required")
private InheritedBoolean contributorAgreements;
@Option(name = "--signed-off-by", usage = "if signed-off-by is required")
private InheritedBoolean signedOffBy;
@Option(name = "--content-merge", usage = "allow automatic conflict resolving within files")
private InheritedBoolean contentMerge;
@Option(name = "--change-id", usage = "if change-id is required")
private InheritedBoolean requireChangeID;
@Option(name = "--use-contributor-agreements", aliases = {"--ca"}, usage = "if contributor agreement is required")
private Boolean contributorAgreements;
void setUseContributorArgreements(boolean on) {
contributorAgreements = InheritedBoolean.TRUE;
}
@Option(name = "--no-contributor-agreements", aliases = {"--nca"}, usage = "if contributor agreement is not required")
private Boolean noContributorAgreements;
void setNoContributorArgreements(boolean on) {
contributorAgreements = InheritedBoolean.FALSE;
}
@Option(name = "--use-signed-off-by", aliases = {"--so"}, usage = "if signed-off-by is required")
private Boolean signedOffBy;
void setUseSignedOffBy(boolean on) {
signedOffBy = InheritedBoolean.TRUE;
}
@Option(name = "--no-signed-off-by", aliases = {"--nso"}, usage = "if signed-off-by is not required")
private Boolean noSignedOffBy;
void setNoSignedOffBy(boolean on) {
signedOffBy = InheritedBoolean.FALSE;
}
@Option(name = "--use-content-merge", usage = "allow automatic conflict resolving within files")
private Boolean contentMerge;
void setUseContentMerge(boolean on) {
contentMerge = InheritedBoolean.TRUE;
}
@Option(name = "--no-content-merge", usage = "don't allow automatic conflict resolving within files")
private Boolean noContentMerge;
void setNoContentMerge(boolean on) {
contentMerge = InheritedBoolean.FALSE;
}
@Option(name = "--require-change-id", aliases = {"--id"}, usage = "if change-id is required")
private Boolean requireChangeID;
void setRequireChangeId(boolean on) {
requireChangeID = InheritedBoolean.TRUE;
}
@Option(name = "--no-change-id", aliases = {"--nid"}, usage = "if change-id is not required")
private Boolean noRequireChangeID;
void setNoChangeId(boolean on) {
requireChangeID = InheritedBoolean.FALSE;
}
@Option(name = "--project-state", aliases = {"--ps"}, usage = "project's visibility state")
private State state;
@ -85,7 +114,6 @@ final class SetProjectCommand extends SshCommand {
@Override
protected void run() throws Failure {
validate();
Project ctlProject = projectControl.getProject();
Project.NameKey nameKey = ctlProject.getNameKey();
String name = ctlProject.getName();
@ -97,38 +125,27 @@ final class SetProjectCommand extends SshCommand {
ProjectConfig config = ProjectConfig.read(md);
Project project = config.getProject();
project.setRequireChangeID(requireChangeID != null ? requireChangeID
: project.isRequireChangeID());
project.setRequireChangeID(noRequireChangeID != null
? !noRequireChangeID : project.isRequireChangeID());
project.setSubmitType(submitType != null ? submitType : project
.getSubmitType());
project.setUseContentMerge(contentMerge != null ? contentMerge
: project.isUseContentMerge());
project.setUseContentMerge(noContentMerge != null ? !noContentMerge
: project.isUseContentMerge());
project.setUseContributorAgreements(contributorAgreements != null
? contributorAgreements : project.isUseContributorAgreements());
project.setUseContributorAgreements(noContributorAgreements != null
? !noContributorAgreements : project.isUseContributorAgreements());
project.setUseSignedOffBy(signedOffBy != null ? signedOffBy : project
.isUseSignedOffBy());
project.setUseContentMerge(noSignedOffBy != null ? !noSignedOffBy
: project.isUseContentMerge());
project.setDescription(projectDescription != null ? projectDescription
: project.getDescription());
project.setState(state != null ? state : project.getState());
if (requireChangeID != null) {
project.setRequireChangeID(requireChangeID);
}
if (submitType != null) {
project.setSubmitType(submitType);
}
if (contentMerge != null) {
project.setUseContentMerge(contentMerge);
}
if (contributorAgreements != null) {
project.setUseContributorAgreements(contributorAgreements);
}
if (signedOffBy != null) {
project.setUseSignedOffBy(signedOffBy);
}
if (projectDescription != null) {
project.setDescription(projectDescription);
}
if (state != null) {
project.setState(state);
}
md.setMessage("Project settings updated");
config.commit(md);
} finally {
@ -154,18 +171,4 @@ final class SetProjectCommand extends SshCommand {
throw new UnloggedFailure(1, err.toString());
}
}
private void validate() throws UnloggedFailure {
checkExclusivity(contentMerge, "--use-content-merge",
noContentMerge, "--no-content-merge");
checkExclusivity(contributorAgreements, "--use-contributor-agreements",
noContributorAgreements, "--no-contributor-agreements");
checkExclusivity(signedOffBy, "--use-signed-off-by",
noSignedOffBy, "--no-signed-off-by");
checkExclusivity(requireChangeID, "--require-change-id",
noRequireChangeID, "--no-change-id");
}
}