Implementing Gitflow with TeamForge and Gerrit. Part II – Repo Category HOWTO

In this blog post series we talk about access right mappings between TeamForge and its Gerrit integration. The topics covered by each part are:

 

Target audience: Project admins, Gerrit administrators using our TeamForge integration.

In the first part of this serie, we have looked into Gitflow’s branching model, configured it in TeamForge via a user-defined repo category called gitflow and verified that it actually works as expected. Our assumption was that the gitflow category was already available. Now, let’s have a look what to do if such category does not exist yet, so there is a need to create it. In other words we will go through the process of repo category creation.

 

First step is to create a new, user-defined repo category and name it. Let’s go to the /opt/collabnet/gerrit/etc/ directory and edit the TeamForgeMappings.xml file located there. Note that this file replaces the gerritforge.mappings from the Gerrit 2.1-based version of our integration. More informations about TeamForgeMappings.xml can be found here and in its XSD schema definition.

 

For a start we will add a new element RepoCategory named gitflow with four children – at the moment it is just an empty skeleton to fill it in later. Here how it looks:

<RepoCategory name="gitflow" keepRightsAddedInGerrit="false">
 <ScmAdmin></ScmAdmin>
 <ScmDeleteView></ScmDeleteView>
 <ScmCommitView></ScmCommitView>
 <ScmViewOnly></ScmViewOnly>
</RepoCategory>

As you see our skeleton consist of a RepoCategory element which contains four sub-elements that corresponds to Source Code Admin, Delete/View, Commit/View and View Only permissions. At that point, it should be pretty obvious where to put the Gerrit access rights corresponding to our permissions: Source Code Admin corresponds to <ScmAdmin> element, Delete/View to <ScmDeleteView> and so on.

 

Let’s start with the Observer and <ScmViewOnly> element. As you know from the previous post the Observer has View only permission in TeamForge and we want to map it into Read access right in Gerrit. This is pretty straightforward one-to-one mapping:

<ScmViewOnly>
 <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
</ScmViewOnly>

Next is the Developer, so we need to provide the content of <ScmCommitView> element, and it is where the things are getting more interesting.

What kind of permissions the developer needs?

First, we will grant him read access to everything. We want him to have a full access to feature and hotfix branches too. That will require Create Reference and Push with force permissions plus Push Merges Commit. He will also have Push access to develop and release/* branches, with ability to push merges but without possibility to rewrite them (no force push).

We want to allow the Developer to push merges to all the branches he has push access. The easiest way to achieve that is to add Push Merge Commits for refs/* as this permission has effect only on branches where the Developer is able to push anyway. If you want to be more restrictive about this permission please make sure you read about this bug in Gerrit. Last but not least, we will give the Developer Forge Author permission, which in my opinion just makes sense.

Let’s have a look at all permissions and the corresponding mapping file entries:

Permission Mappings file entries
Read <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
Create reference and push with force to feature branches <GerritCreateReference value="ALLOW" refPattern="refs/heads/hotfix/*" exclusive="false"/>
<GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/hotfix/*" exclusive="false"/>
Create reference and push with force to hotfix branches <GerritCreateReference value="ALLOW" refPattern="refs/heads/hotfix/*" exclusive="false"/>
<GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/hotfix/*" exclusive="false"/>
Create reference and push with no force to release branches <GerritCreateReference value="ALLOW" refPattern="refs/heads/release/*" exclusive="false"/>
<GerritPush forcePush="false" value="ALLOW" refPattern="refs/heads/release/*" exclusive="false"/>
Push with no force to develop branch <GerritPush forcePush="false" value="ALLOW" refPattern="refs/heads/develop" exclusive="false"/>
Allow to push merges to any branches <GerritCreateReference value="ALLOW" refPattern="refs/*" exclusive="false"/>
Forge Author <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>

 

Here goes the complete code snippet for Commit/View permission:

<ScmCommitView>
 <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/hotfix/*" 
  exclusive="false"/>
 <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/feature/*" 
  exclusive="false"/>
 <GerritPush forcePush="false" value="ALLOW" refPattern="refs/heads/develop" 
  exclusive="false"/>
 <GerritPush forcePush="false" value="ALLOW" refPattern="refs/heads/release/*" 
  exclusive="false"/>
 <GerritCreateReference value="ALLOW" refPattern="refs/heads/hotfix/*" 
  exclusive="false"/>
 <GerritCreateReference value="ALLOW" refPattern="refs/heads/feature/*" 
  exclusive="false"/>
 <GerritCreateReference value="ALLOW" refPattern="refs/heads/release/*" 
  exclusive="false"/>
 <GerritPushMerges value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
</ScmCommitView>

 

Now it is time to configure access rights for Release Manager. We will give him all the rights the Developer has plus the possibility to force push on develop branch, master branch as well as release/* branches. Hi will be also able to push signed and annotated tags. Thanks to our history protection feature we are not too concerned about rewriting history. Let’s have a look at the newly added permissions:

Permission Mappings file entries
Force push on develop and master branches <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/develop" exclusive="false"/>
<GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/master" exclusive="false"/>
Force push on release/* branches <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/release/*" exclusive="false"/>
Push signed and annotated tags <GerritPushAnnotatedTag forcePush="truee" value="ALLOW" refPattern="refs/tags/*" exclusive="false"/>
<GerritPushSignedTag value="ALLOW" refPattern="refs/tags/*" exclusive="false"/>

 

Finally, we have the complete Delete/View code snippet:

<ScmDeleteView>
 <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/> 
 <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/hotfix/*"
  exclusive="false"/>
 <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/feature/*"
  exclusive="false"/>
 <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/develop"
  exclusive="false"/>
 <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/master"
  exclusive="false"/>
 <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/release/*"
  exclusive="false"/>
 <GerritCreateReference value="ALLOW" refPattern="refs/heads/hotfix/*"
  exclusive="false"/>
 <GerritCreateReference value="ALLOW" refPattern="refs/heads/feature/*"
  exclusive="false"/>
 <GerritPushMerges value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritPushAnnotatedTag forcePush="false" value="ALLOW" refPattern="refs/tags/*"
  exclusive="false"/>
 <GerritPushSignedTag value="ALLOW" refPattern="refs/tags/*" exclusive="false"/>
</ScmDeleteView>

We will not spend much time on Admin, as the one which fits our needs can be taken as-is from our default repo category. Here it goes:

<ScmAdmin>
 <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritPush forcePush="true" value="ALLOW" refPattern="refs/*"    exclusive="false"/>
 <GerritCreateReference value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritForgeCommitterIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritForgeServerIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritOwner value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritAbandon value="ALLOW" refPattern="refs/*" exclusive="false"/>
 <GerritPushMerges value="ALLOW" refPattern="refs/for/refs/*" exclusive="false"/>
 <GerritPushAnnotatedTag forcePush="false" value="ALLOW" refPattern="refs/tags/*"
  exclusive="false"/>
 <GerritPushSignedTag value="ALLOW" refPattern="refs/tags/*" exclusive="false"/>
</ScmAdmin>

Well, now we are done with all permissions. Let’s put it all together. Our gitflow category looks like this:

<RepoCategory name="gitflow" keepRightsAddedInGerrit="false">
 <ScmAdmin>
  <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritPush forcePush="true" value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritCreateReference value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritForgeCommitterIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritForgeServerIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritOwner value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritAbandon value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritPushMerges value="ALLOW" refPattern="refs/for/refs/*" exclusive="false"/>
  <GerritPushAnnotatedTag forcePush="false" value="ALLOW" refPattern="refs/tags/*"
   exclusive="false"/>
  <GerritPushSignedTag value="ALLOW" refPattern="refs/tags/*" exclusive="false"/>
 </ScmAdmin>
 <ScmDeleteView>
  <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/hotfix/*"
   exclusive="false"/>
  <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/feature/*"
   exclusive="false"/>
  <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/develop"
   exclusive="false"/>
  <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/master"
   exclusive="false"/>
  <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/release/*"
   exclusive="false"/>
  <GerritCreateReference value="ALLOW" refPattern="refs/heads/hotfix/*"
   exclusive="false"/>
  <GerritCreateReference value="ALLOW" refPattern="refs/heads/feature/*"
   exclusive="false"/>
  <GerritPushMerges value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritPushAnnotatedTag forcePush="false" value="ALLOW" refPattern="refs/tags/*"
   exclusive="false"/>
  <GerritPushSignedTag value="ALLOW" refPattern="refs/tags/*" exclusive="false"/>
 </ScmDeleteView>
 <ScmCommitView>
  <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/hotfix/*"
   exclusive="false"/>
  <GerritPush forcePush="true" value="ALLOW" refPattern="refs/heads/feature/*"
   exclusive="false"/>
  <GerritPush forcePush="false" value="ALLOW" refPattern="refs/heads/develop"
   exclusive="false"/>
  <GerritPush forcePush="false" value="ALLOW" refPattern="refs/heads/release/*"
   exclusive="false"/>
  <GerritCreateReference value="ALLOW" refPattern="refs/heads/hotfix/*"
   exclusive="false"/>
  <GerritCreateReference value="ALLOW" refPattern="refs/heads/feature/*"
   exclusive="false"/>
  <GerritPushMerges value="ALLOW" refPattern="refs/*" exclusive="false"/>
  <GerritForgeAuthorIdentity value="ALLOW" refPattern="refs/*" exclusive="false"/>
 </ScmCommitView>
 <ScmViewOnly>
  <GerritRead value="ALLOW" refPattern="refs/*" exclusive="false"/>
 </ScmViewOnly>
</RepoCategory>

 

You can download the above page fragment from here.

Now, we are done with the editing so it is time to add our newly created gitflow repo category to the TeamForgeMappings.xml file located in /opt/collabnet/gerrit/etc/ directory, save the file and restart Gerrit.

At that point, after reading both parts of this blog series, you have all you need to define your own repo categories and are able to use them across your projects.

In the last part of this series we will look at the alternative approaches to implement branch based permissions that are available in our Gerrit 2.6 integration, what are the advantages and drawbacks of them in comparison to a user-defined category and when to use which approach.

What’s your experience with user-defined repo categories? Do you need help or have any questions? Any feedback is welcome.

Eryk Szymanski

Eryk is CollabNet’s Development Manager leading Git and Gerrit related development efforts. He has over 20 years of engineering and management experience ranging from start-ups to medium-size enterprises. Eryk holds Master degree in Computer Science and is Certified Scrum Master.

Posted in Git, TeamForge

Leave a Reply

Your email address will not be published. Required fields are marked *

*