"Submit on Push" option on Submit Perm.
Add "Submit on Push" option to the Submit Permission and remove the "refs/for/"-way of allowing this. This change introduces a "Submit on Push" option for the Submit permission, which will enable the %submit push parameter. Any "submit refs/for/*" permission will be automatially migrated when the config is loaded. Change-Id: I6106ecf289bef541fa273d3deebead7a3a9710ac
This commit is contained in:
		| @@ -795,8 +795,7 @@ above) must enable submit, and also must not block it.  See above for | |||||||
| details on each label. | details on each label. | ||||||
|  |  | ||||||
| To link:user-upload.html#auto_merge[immediately submit a change on push] | To link:user-upload.html#auto_merge[immediately submit a change on push] | ||||||
| the caller needs to have the Submit permission on `refs/for/<ref>` | the caller needs to enable the "Submit on Push" option. | ||||||
| (e.g. on `refs/for/refs/heads/master`). |  | ||||||
|  |  | ||||||
| Submitting to the `refs/meta/config` branch is only allowed to project | Submitting to the `refs/meta/config` branch is only allowed to project | ||||||
| owners. Any explicit submit permissions for non-project-owners on this | owners. Any explicit submit permissions for non-project-owners on this | ||||||
|   | |||||||
| @@ -58,7 +58,7 @@ limitations under the License. | |||||||
|     position: absolute; |     position: absolute; | ||||||
|     top: 0; |     top: 0; | ||||||
|     right: 36px; |     right: 36px; | ||||||
|     width: 7em; |     width: 9em; | ||||||
|     font-size: 80%; |     font-size: 80%; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ package com.google.gerrit.client.admin; | |||||||
|  |  | ||||||
| import static com.google.gerrit.common.data.Permission.EDIT_TOPIC_NAME; | import static com.google.gerrit.common.data.Permission.EDIT_TOPIC_NAME; | ||||||
| import static com.google.gerrit.common.data.Permission.PUSH; | import static com.google.gerrit.common.data.Permission.PUSH; | ||||||
|  | import static com.google.gerrit.common.data.Permission.SUBMIT; | ||||||
|  |  | ||||||
| import com.google.gerrit.client.Dispatcher; | import com.google.gerrit.client.Dispatcher; | ||||||
| import com.google.gerrit.client.Gerrit; | import com.google.gerrit.client.Gerrit; | ||||||
| @@ -139,15 +140,23 @@ public class PermissionRuleEditor extends Composite | |||||||
|     initWidget(uiBinder.createAndBindUi(this)); |     initWidget(uiBinder.createAndBindUi(this)); | ||||||
|  |  | ||||||
|     String name = permission.getName(); |     String name = permission.getName(); | ||||||
|     boolean canForce = PUSH.equals(name); |     boolean canForce = true; | ||||||
|     if (canForce) { |     switch (name) { | ||||||
|       String ref = section.getName(); |       case SUBMIT: | ||||||
|       canForce = !ref.startsWith("refs/for/") && !ref.startsWith("^refs/for/"); |         force.setText(PermissionRule.FORCE_SUBMIT); | ||||||
|       force.setText(PermissionRule.FORCE_PUSH); |         break; | ||||||
|     } else { |       case EDIT_TOPIC_NAME: | ||||||
|       canForce = EDIT_TOPIC_NAME.equals(name); |         force.setText(PermissionRule.FORCE_EDIT); | ||||||
|       force.setText(PermissionRule.FORCE_EDIT); |         break; | ||||||
|  |       case PUSH: | ||||||
|  |         force.setText(PermissionRule.FORCE_PUSH); | ||||||
|  |         String ref = section.getName(); | ||||||
|  |         canForce = !ref.startsWith("refs/for/") && !ref.startsWith("^refs/for/"); | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         canForce = false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     force.setVisible(canForce); |     force.setVisible(canForce); | ||||||
|     force.setEnabled(!readOnly); |     force.setEnabled(!readOnly); | ||||||
|     action.getElement().setPropertyBoolean("disabled", readOnly); |     action.getElement().setPropertyBoolean("disabled", readOnly); | ||||||
|   | |||||||
| @@ -55,7 +55,7 @@ limitations under the License. | |||||||
|     position: absolute; |     position: absolute; | ||||||
|     top: 0; |     top: 0; | ||||||
|     right: 36px; |     right: 36px; | ||||||
|     width: 7em; |     width: 9em; | ||||||
|     font-size: 80%; |     font-size: 80%; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ | |||||||
| package com.google.gerrit.common.data; | package com.google.gerrit.common.data; | ||||||
|  |  | ||||||
| public class PermissionRule implements Comparable<PermissionRule> { | public class PermissionRule implements Comparable<PermissionRule> { | ||||||
|  |   public static final String FORCE_SUBMIT = "Submit on Push"; | ||||||
|   public static final String FORCE_PUSH = "Force Push"; |   public static final String FORCE_PUSH = "Force Push"; | ||||||
|   public static final String FORCE_EDIT = "Force Edit"; |   public static final String FORCE_EDIT = "Force Edit"; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -141,7 +141,7 @@ class ChangeControl { | |||||||
|  |  | ||||||
|   /** Can this user rebase this change? */ |   /** Can this user rebase this change? */ | ||||||
|   private boolean canRebase(ReviewDb db) throws OrmException { |   private boolean canRebase(ReviewDb db) throws OrmException { | ||||||
|     return (isOwner() || refControl.canSubmit(isOwner()) || refControl.canRebase()) |     return (isOwner() || refControl.canSubmit(isOwner(), false) || refControl.canRebase()) | ||||||
|         && refControl.asForRef().testOrFalse(RefPermission.CREATE_CHANGE) |         && refControl.asForRef().testOrFalse(RefPermission.CREATE_CHANGE) | ||||||
|         && !isPatchSetLocked(db); |         && !isPatchSetLocked(db); | ||||||
|   } |   } | ||||||
| @@ -374,7 +374,7 @@ class ChangeControl { | |||||||
|           case RESTORE: |           case RESTORE: | ||||||
|             return canRestore(db()); |             return canRestore(db()); | ||||||
|           case SUBMIT: |           case SUBMIT: | ||||||
|             return refControl.canSubmit(isOwner()); |             return refControl.canSubmit(isOwner(), false); | ||||||
|  |  | ||||||
|           case REMOVE_REVIEWER: |           case REMOVE_REVIEWER: | ||||||
|           case SUBMIT_AS: |           case SUBMIT_AS: | ||||||
|   | |||||||
| @@ -108,7 +108,7 @@ class RefControl { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** @return true if this user can submit patch sets to this ref */ |   /** @return true if this user can submit patch sets to this ref */ | ||||||
|   boolean canSubmit(boolean isChangeOwner) { |   boolean canSubmit(boolean isChangeOwner, boolean force) { | ||||||
|     if (RefNames.REFS_CONFIG.equals(refName)) { |     if (RefNames.REFS_CONFIG.equals(refName)) { | ||||||
|       // Always allow project owners to submit configuration changes. |       // Always allow project owners to submit configuration changes. | ||||||
|       // Submitting configuration changes modifies the access control |       // Submitting configuration changes modifies the access control | ||||||
| @@ -117,7 +117,7 @@ class RefControl { | |||||||
|       // granting of powers beyond submitting to the configuration. |       // granting of powers beyond submitting to the configuration. | ||||||
|       return projectControl.isOwner(); |       return projectControl.isOwner(); | ||||||
|     } |     } | ||||||
|     return canPerform(Permission.SUBMIT, isChangeOwner, false); |     return canPerform(Permission.SUBMIT, isChangeOwner, force); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /** @return true if this user can force edit topic names. */ |   /** @return true if this user can force edit topic names. */ | ||||||
| @@ -494,7 +494,7 @@ class RefControl { | |||||||
|           return canPerform(refPermissionName(perm)); |           return canPerform(refPermissionName(perm)); | ||||||
|  |  | ||||||
|         case UPDATE_BY_SUBMIT: |         case UPDATE_BY_SUBMIT: | ||||||
|           return projectControl.controlForRef(MagicBranch.NEW_CHANGE + refName).canSubmit(true); |           return canSubmit(true, true); | ||||||
|  |  | ||||||
|         case READ_PRIVATE_CHANGES: |         case READ_PRIVATE_CHANGES: | ||||||
|           return canPerform(Permission.VIEW_PRIVATE_CHANGES); |           return canPerform(Permission.VIEW_PRIVATE_CHANGES); | ||||||
|   | |||||||
| @@ -756,6 +756,23 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError. | |||||||
|       convertedPerm.addAll(perm.getRules()); |       convertedPerm.addAll(perm.getRules()); | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|  |     if (isRefsForExclusively(refName) && perm.getName().equals(Permission.SUBMIT)) { | ||||||
|  |       // We only migrate "Submit" if is exclusively added to refs/for/. | ||||||
|  |       // We do this because when the new "Submit on Push" force | ||||||
|  |       // option is added, it is somewhat likely that people will start specify | ||||||
|  |       // Submit on refs/* (something which previously had to be avoided if the | ||||||
|  |       // user did not want to enable "Submit on push"). | ||||||
|  |       // Also, before this change, the documentation mentioned | ||||||
|  |       // explicitly that "refs/for/*" will enable "Submit on push". | ||||||
|  |       AccessSection migratedAs = getAccessSection(unRefsFor(refName), true); | ||||||
|  |       Permission migratedPerm = migratedAs.getPermission(Permission.SUBMIT, true); | ||||||
|  |       migratedPerm.setExclusiveGroup(perm.getExclusiveGroup()); | ||||||
|  |       for (PermissionRule rule : perm.getRules()) { | ||||||
|  |         PermissionRule migratedRule = migratedPerm.getRule(rule.getGroup(), true); | ||||||
|  |         migratedRule.setForce(true); | ||||||
|  |       } | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -129,11 +129,11 @@ public class RefControlTest { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private void assertCanSubmit(String ref, ProjectControl u) { |   private void assertCanSubmit(String ref, ProjectControl u) { | ||||||
|     assertThat(u.controlForRef(ref).canSubmit(false)).named("can submit " + ref).isTrue(); |     assertThat(u.controlForRef(ref).canSubmit(false, false)).named("can submit " + ref).isTrue(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private void assertCannotSubmit(String ref, ProjectControl u) { |   private void assertCannotSubmit(String ref, ProjectControl u) { | ||||||
|     assertThat(u.controlForRef(ref).canSubmit(false)).named("can submit " + ref).isFalse(); |     assertThat(u.controlForRef(ref).canSubmit(false, false)).named("can submit " + ref).isFalse(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private void assertCanUpload(ProjectControl u) { |   private void assertCanUpload(ProjectControl u) { | ||||||
|   | |||||||
| @@ -614,6 +614,64 @@ public class ProjectConfigTest extends GerritBaseTests { | |||||||
|         .isEqualTo(Lists.newArrayList(new PermissionRule(developers))); |         .isEqualTo(Lists.newArrayList(new PermissionRule(developers))); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   @Test | ||||||
|  |   public void readConfigSubmitRefsForStarIsMigrated() throws Exception { | ||||||
|  |     RevCommit rev = | ||||||
|  |         tr.commit() | ||||||
|  |             .add("groups", group(developers)) | ||||||
|  |             .add("project.config", "[access \"refs/for/*\"]\n" + "  submit = group Developers\n") | ||||||
|  |             .create(); | ||||||
|  |  | ||||||
|  |     ProjectConfig cfg = read(rev); | ||||||
|  |     AccessSection as = cfg.getAccessSection("refs/for/*"); | ||||||
|  |     assertThat(as).isNull(); | ||||||
|  |  | ||||||
|  |     as = cfg.getAccessSection("refs/*"); | ||||||
|  |     assertThat(as.getPermission(Permission.SUBMIT, false)).isNotNull(); | ||||||
|  |     PermissionRule rule = new PermissionRule(developers); | ||||||
|  |     rule.setForce(true); | ||||||
|  |     assertThat(as.getPermission(Permission.SUBMIT, false).getRules()) | ||||||
|  |         .isEqualTo(Lists.newArrayList(rule)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Test | ||||||
|  |   public void readConfigSubmitRefsStarIsNotMigrated() throws Exception { | ||||||
|  |     RevCommit rev = | ||||||
|  |         tr.commit() | ||||||
|  |             .add("groups", group(developers)) | ||||||
|  |             .add("project.config", "[access \"refs/*\"]\n" + "  submit = group Developers\n") | ||||||
|  |             .create(); | ||||||
|  |  | ||||||
|  |     ProjectConfig cfg = read(rev); | ||||||
|  |     AccessSection as = cfg.getAccessSection("refs/*"); | ||||||
|  |     assertThat(as).isNotNull(); | ||||||
|  |     PermissionRule rule = new PermissionRule(developers); | ||||||
|  |     assertThat(as.getPermission(Permission.SUBMIT, false).getRules()) | ||||||
|  |         .isEqualTo(Lists.newArrayList(rule)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @Test | ||||||
|  |   public void readConfigSubmitRefsForStarIsMigratedIntoExistingPermission() throws Exception { | ||||||
|  |     RevCommit rev = | ||||||
|  |         tr.commit() | ||||||
|  |             .add("groups", group(developers)) | ||||||
|  |             .add( | ||||||
|  |                 "project.config", | ||||||
|  |                 "[access \"refs/*\"]\n" | ||||||
|  |                     + "  submit = group Developers\n" | ||||||
|  |                     + "[access \"refs/for/*\"]\n" | ||||||
|  |                     + "  submit = group Developers\n") | ||||||
|  |             .create(); | ||||||
|  |  | ||||||
|  |     ProjectConfig cfg = read(rev); | ||||||
|  |     AccessSection as = cfg.getAccessSection("refs/*"); | ||||||
|  |     assertThat(as).isNotNull(); | ||||||
|  |     PermissionRule rule = new PermissionRule(developers); | ||||||
|  |     rule.setForce(true); | ||||||
|  |     assertThat(as.getPermission(Permission.SUBMIT, false).getRules()) | ||||||
|  |         .isEqualTo(Lists.newArrayList(rule)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   @Test |   @Test | ||||||
|   public void readCommentLinkMatchButNoHtmlOrLink() throws Exception { |   public void readCommentLinkMatchButNoHtmlOrLink() throws Exception { | ||||||
|     RevCommit rev = |     RevCommit rev = | ||||||
|   | |||||||
| @@ -56,6 +56,17 @@ | |||||||
|     }, |     }, | ||||||
|   ]; |   ]; | ||||||
|  |  | ||||||
|  |   const FORCE_SUBMIT_OPTIONS = [ | ||||||
|  |     { | ||||||
|  |       name: 'No Submit on Push', | ||||||
|  |       value: false, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'Submit on Push', | ||||||
|  |       value: true, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  |  | ||||||
|   Polymer({ |   Polymer({ | ||||||
|     is: 'gr-rule-editor', |     is: 'gr-rule-editor', | ||||||
|  |  | ||||||
| @@ -114,7 +125,8 @@ | |||||||
|  |  | ||||||
|     _computeForce(permission) { |     _computeForce(permission) { | ||||||
|       return this.permissionValues.push.id === permission || |       return this.permissionValues.push.id === permission || | ||||||
|           this.permissionValues.editTopicName.id === permission; |           this.permissionValues.editTopicName.id === permission || | ||||||
|  |           this.permissionValues.submit.id === permission; | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     _computeForceClass(permission) { |     _computeForceClass(permission) { | ||||||
| @@ -156,6 +168,8 @@ | |||||||
|         return FORCE_PUSH_OPTIONS; |         return FORCE_PUSH_OPTIONS; | ||||||
|       } else if (permission === this.permissionValues.editTopicName.id) { |       } else if (permission === this.permissionValues.editTopicName.id) { | ||||||
|         return FORCE_EDIT_OPTIONS; |         return FORCE_EDIT_OPTIONS; | ||||||
|  |       } else if (permission === this.permissionValues.submit.id) { | ||||||
|  |         return FORCE_SUBMIT_OPTIONS; | ||||||
|       } |       } | ||||||
|       return []; |       return []; | ||||||
|     }, |     }, | ||||||
|   | |||||||
| @@ -71,6 +71,16 @@ limitations under the License. | |||||||
|                 value: true, |                 value: true, | ||||||
|               }, |               }, | ||||||
|             ]; |             ]; | ||||||
|  |             const FORCE_SUBMIT_OPTIONS = [ | ||||||
|  |               { | ||||||
|  |                 name: 'No Submit on Push', | ||||||
|  |                 value: false, | ||||||
|  |               }, | ||||||
|  |               { | ||||||
|  |                 name: 'Submit on Push', | ||||||
|  |                 value: true, | ||||||
|  |               }, | ||||||
|  |             ]; | ||||||
|             let permission = 'push'; |             let permission = 'push'; | ||||||
|             assert.isTrue(element._computeForce(permission)); |             assert.isTrue(element._computeForce(permission)); | ||||||
|             assert.equal(element._computeForceClass(permission), 'force'); |             assert.equal(element._computeForceClass(permission), 'force'); | ||||||
| @@ -82,6 +92,11 @@ limitations under the License. | |||||||
|             assert.deepEqual(element._computeForceOptions(permission), |             assert.deepEqual(element._computeForceOptions(permission), | ||||||
|                 FORCE_EDIT_OPTIONS); |                 FORCE_EDIT_OPTIONS); | ||||||
|             permission = 'submit'; |             permission = 'submit'; | ||||||
|  |             assert.isTrue(element._computeForce(permission)); | ||||||
|  |             assert.equal(element._computeForceClass(permission), 'force'); | ||||||
|  |             assert.deepEqual(element._computeForceOptions(permission), | ||||||
|  |                 FORCE_SUBMIT_OPTIONS); | ||||||
|  |             permission = 'editHashtags'; | ||||||
|             assert.isFalse(element._computeForce(permission)); |             assert.isFalse(element._computeForce(permission)); | ||||||
|             assert.equal(element._computeForceClass(permission), ''); |             assert.equal(element._computeForceClass(permission), ''); | ||||||
|             assert.deepEqual(element._computeForceOptions(permission), []); |             assert.deepEqual(element._computeForceOptions(permission), []); | ||||||
| @@ -124,7 +139,7 @@ limitations under the License. | |||||||
|           {action: 'ALLOW', force: false}); |           {action: 'ALLOW', force: false}); | ||||||
|         permission = 'submit'; |         permission = 'submit'; | ||||||
|         assert.deepEqual(element._getDefaultRuleValues(permission, label), |         assert.deepEqual(element._getDefaultRuleValues(permission, label), | ||||||
|             {action: 'ALLOW'}); |             {action: 'ALLOW', force: false}); | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|       test('_setDefaultRuleValues', () => { |       test('_setDefaultRuleValues', () => { | ||||||
| @@ -210,7 +225,7 @@ limitations under the License. | |||||||
|         assert.equal(element.$.action.bindValue, element.rule.value.action); |         assert.equal(element.$.action.bindValue, element.rule.value.action); | ||||||
|         assert.isNotOk(Polymer.dom(element.root).querySelector('#labelMin')); |         assert.isNotOk(Polymer.dom(element.root).querySelector('#labelMin')); | ||||||
|         assert.isNotOk(Polymer.dom(element.root).querySelector('#labelMax')); |         assert.isNotOk(Polymer.dom(element.root).querySelector('#labelMax')); | ||||||
|         assert.isFalse(element.$.force.classList.contains('force')); |         assert.isTrue(element.$.force.classList.contains('force')); | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|       test('modify and cancel restores original values', () => { |       test('modify and cancel restores original values', () => { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Gustaf Lundh
					Gustaf Lundh