In this blog post series we talk about access right mappings between TeamForge and its Gerrit integration. The topics covered by each part are:
- in Part I: What Gitflow is, and how to best use it with TeamForge/Gerrit user defined repository policies
- in Part II: How to implement your own user-defined repo category with branch based permissions in TeamForge 7.1 and Gerrit 2.6.
- in Part III: Alternative approaches to implement branch based permissions (without user-defined repo categories) and when to use which approach.
Target audience: Project admins, Gerrit administrators using our TeamForge integration.
In my post about Managing Git branch level permissions with TeamForge and Gerrit I was using a TeamForge 6.2 + Gerrit 2.1-based integration as a setup. Now, with TeamForge 7.1 and the new Gerrit 2.6-based integration that blog post got outdated as almost everything in this area has changed. To be clear: It still holds for the old setup (Gerrit 2.1) but does not work for the new one (Gerrit 2.6).
So what has changed? Well, pretty much everything. Most importantly, as a result of Access Control changes between Gerrit 2.1 and 2.2 a new mapping file format has been developed. You can read about it here: Easy guide to mappings between Gerrit Access Control and TeamForge Source Code Permissions.
With this knowledge it should be easy now to implement branch level permissions in the new setup (TeamForge 7.x + ctf-git-integration-ng-8.x). But, how to proceed with that?
My first thought was to go through the configuration described in the initial blog post and adjust it so it fits the new setup. But I have a better idea: We will take Gitflow as an example and implement a user-defined repo category that enforces the Gitflow model. I’m quite sure that after this exercise it will be easy for you to migrate the
branch_based category (described in the old blog post) to the TeamForge 7.1 with Gerrit 2.6-based integration.
But first let’s talk a little bit about Gitflow.
I’m not going to dig too deep into details on what Gitflow is. In short: It is a git branching model which is very well described here.
It consists of the following elements:
- Two main branches:
- master – the branch where the source code of
HEADalways reflects a production-ready state.
- develop – the branch where the source code of
HEADalways reflects a state with the latest delivered development changes for the next release.
- master – the branch where the source code of
- And some supporting branches:
- feature branches – for developing new features – merges into develop branch.
- release branches – for preparing releases – merges into develop and master.
- hotfix branches – for developing hotfixes – merges into develop and master.
One could point out that there are also support branches – that is true but I’m going to skip this feature as at the moment of writing it is still experimental and not recommended to use.
Note: If your team is using code review feature of Gerrit then Gitflow branching model is probably not the best choice. On the other hand it seems that this model just works for many people so it is a good, real-life use case for user-defined repo category.
It is important to know that using Gitflow with TeamForge + Git/Gerrit integration is possible out of the box, as our pre-defined,
default repo category will do the job. But what if for any reasons it is necessary to restrict permissions to some branches?
Imagine your company is going to use TeamForge 7.1 with Gerrit 2.6 integration and wants to define some roles that enforces the Gitflow branching model for one or more repositories in a more secure way. In particular the company policy says that only Release Managers are able to modify the master branch and/or create releases. Additionally it requires that developers are not able to overwrite history of remote develop and release branches. Another requirement is to provide some people with possibility to view the repositories in read-only mode. Finally, the policy allows the system administrators to perform virtually any kind of action.
By the way, this is a rather typical example of branch based permissions. So after going through this setup it should be easy to see how to deal with any other branch based permissions policy requests.
Let’s get started then: at this point we will just assume that we already have a user-defined repository category called gitflow. For this blog post I have create one which is ready to use. How to create this category will be covered in the second blog post from this series, but at the moment all we need is that the gitflow category is there.
First, let’s have a look on the steps required to make a user-defined category to work:
- Define project roles in the TeamForge project in question.
- Assign TF SCM permissions to the newly defined roles.
- Assign some users to the roles for verification purposes.
- Make sure that the required category is available in TF.
- Adjust one or more repositories to use this category.
- Verify that it works.
Please note that steps 7 and 8 are not covered by this blog post.
So let’s start with the roles. We will define four of them:
- Observer – a person that has read only access to repository.
- Developer – develops features and hotfixes – has full access to feature and hotfix branches and can push to develop branch.
- Release Manager – merges features and hotfixes into develop and master branches, manages release branches.
- Project Admin – project administrator.
TeamForge->Project Admin->Permissions and do the needful. You should end up with something like this:
Now, it is time to assign TF source code permissions to those roles. As you might already know in TeamForge there are four SCM permission clusters that control access to the source code. Those are Source Code Admin, Delete/View, Commit/View and View Only permissions.
In our case we will use each of them for a different role. Here is how:
|Role||Source code permission|
|Release Manager||Delete/View + Commit/View|
|Project Administrator||Source Code Admin|
So, back to TeamForge, click on the given role name, for example Developer and then choose a Source Code menu item on the left panel. Let’s have a look:
In the picture you can see the Developer role with Commit/View permission already assigned. We will have to go through all roles and assign permissions accordingly.
Let’s assign some users to these roles. For testing purposes we will have four users, one for each role. Two steps are needed to accomplish that:
- Add the users to the project in question.
- Assign users to the roles.
Once you’re done your user-role matrix should look like this:
Once the user-role matrix has been created it’s time to make Gitflow category available to our integration. As already mentioned before the second blog post in this series will go into details on how to create this category. At the moment, all you need to do is to download this RepoCategory xml fragment and add it to your TeamForgeMappings.xml file located in
/opt/collabnet/gerrit/etc directory. You open the TeamForgeMappings.xml file with your favourite editor and add this fragment at the end of that file, after the last
</RepoCategory> and before the
</TeamForgeGerritMappings> marker. After that you should restart gerrit. If it starts successfully you can be almost sure that everything went fine – to be completely sure we will verify that soon.
The last thing to do is to apply our new, user-defined gitflow repo category to our repository of choice in TeamForge. We go to Source Code menu, edit the repository and perform two changes: change Repository Category to Other: gitflow and enable History Protection:
Note: the names of repo categories are case sensitive, so
gitflow are different. In this blog post we use
gitflow as repo category and Gitflow as a branching model name.
After pressing the
Save button we will click on repository in question to verify that the changes we wanted to apply are reflected in Gerrit:
In the red rectangle you can see that the Repo Category is gitflow and that History Protection is enabled.
It is time to verify how our setup works. Please note that for testing purposes we will not use Gitflow software that is available from here. The reason for it is very simple – we have restricted access to certain branches for certain roles on the Git server. As a result the Gitflow software does not work properly with our configuration – in our setup releases and hotfixes have to be manually merged to master by the Release Manager.
Before we start the verification process we need to create a develop branch on the server. Only admin can do that: either from command line or by going to Gerrit UI, typing it in branches tab of project in question and pressing the Create Branch button:
Note: Gerrit project mentioned above is actually TeamForge repository to which we have applied our configuration.
Now we will have a look on how to work on a feature and how to make a release. Our users are Joe the Developer and Lucy the Release Manager.
Joe works on a feature
Now we will become a developer for a moment to show how to work with features:
$ git clone ssh://joe@personal-centos:29418/gitflow-repo $ cd gitflow-repo $ git config user.name "Joe Smith" $ git config user.email firstname.lastname@example.org
Joe creates our first feature on the base of remote develop branch:
$ git checkout -b feature/feature1 origin/develop Branch feature/feature1 set up to track remote branch develop from origin. Switched to a new branch 'feature/feature1' $ git branch * feature/feature1 master
Feature branch is ready so it is time to write our feature:
$ echo "Feature1" > README.txt $ git add README.txt $ git commit -m "Feature 1" [feature/feature1 2433e2b] Feature1 1 file changed, 1 insertion(+) create mode 100644 README.txt
Now he pushes the feature to the server to share it with other developers:
$ git push Counting objects: 4, done. Writing objects: 100% (3/3), 248 bytes, done. Total 3 (delta 0), reused 0 (delta 0) remote: Processing changes: refs: 1, done To ssh://joe@personal-centos:29418/gitflow-repo * [new branch] HEAD -> feature/feature1
Everything works as expected and Joe is very happy with the feature, so it’s time to merge it into develop branch.
$ git pull origin develop Already up-to-date. $ git checkout develop Switched to branch 'develop' $ git merge --no-ff feature/feature1 Merge made by the 'recursive' strategy. README.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.txt
After that Joe is ready to push to develop branch on
$ git push Counting objects: 1, done. Writing objects: 100% (1/1), 232 bytes, done. Total 1 (delta 0), reused 0 (delta 0) remote: Processing changes: refs: 2, done To ssh://joe@personal-centos:29418/gitflow-repo 2ecc701..1f7c7a6 HEAD -> develop
Again it worked well so Joe can safely remove the feature branch locally:
$ git branch -d feature/feature1 Deleted branch feature/feature1 (was 2433e2b).
and from the
origin (order does not matter here):
$ git push origin :feature/feature1 remote: Processing changes: refs: 1, done To ssh://joe@personal-centos:29418/gitflow-repo - [deleted] feature/feature1
This way Joe got his first feature merged into develop branch.
What will happen, if Joe pushes to master?
$ git push origin HEAD:master Total 0 (delta 0), reused 0 (delta 0) remote: Branch refs/heads/master: remote: You are not allowed to perform this operation. remote: To push into this reference you need 'Push' rights. remote: User: joe remote: Please read the documentation and contact an administrator remote: if you feel the configuration is incorrect remote: Processing changes: refs: 1, done To ssh://joe@personal-centos:29418/gitflow-repo ! [remote rejected] HEAD -> master (prohibited by Gerrit) error: failed to push some refs to 'ssh://joe@personal-centos:29418/gitflow-repo'
It’s not allowed for a developer to write to master branch and Joe is no exception there.
Now Joe tries to delete develop branch:
$ git push origin :refs/heads/develop remote: Branch refs/heads/develop: remote: You need 'Push' rights with the 'Force Push' remote: flag set to delete references. remote: User: joe remote: Please read the documentation and contact an administrator remote: if you feel the configuration is incorrect remote: Processing changes: refs: 1, done To ssh://joe@personal-centos:29418/gitflow-repo ! [remote rejected] develop (cannot delete references) error: failed to push some refs to 'ssh://joe@personal-centos:29418/gitflow-repo'
Deleting a branch or rewriting it requires force push permission which developers do not have. As you can see, a Developer can work on features, but he cannot push to
origin/master nor rewrite
origin/develop branch history.
Lucy makes a release
Now we will have a look on how Lucy can work on a release. Some preparations first:
$ git clone ssh://lucy@personal-centos:29418/gitflow-repo $ cd gitflow-repo $ git config user.name "Lucy Liu" $ git config user.email email@example.com
Release branches are created from the develop branch, so Lucy creates the first release this way:
$ git checkout -b release/release-1.0 origin/develop Branch release/release-1.0 set up to track remote branch develop from origin. Switched to a new branch 'release/release-1.0'
Now Lucy is adding version info to our
README.txt and makes a commit.
$ echo "Version-1.0" >> README.txt $ git add README.txt $ git commit -m "version updated to 1.0" [release-1.0 81fa4e2] version updated to 1.0 1 file changed, 1 insertion(+)
At this point release is ready, so Lucy needs to merge it into the develop and master branch. Just before merging Lucy pushes the release to the
$ git push Counting objects: 5, done. Writing objects: 100% (3/3), 271 bytes, done. Total 3 (delta 0), reused 0 (delta 0) remote: Processing changes: refs: 1, done To ssh://lucy@personal-centos:29418/gitflow-repo * [new branch] HEAD -> release/release-1.0
As you can see that for her it has actually worked. Note, that it wouldn’t work for the developers, as they do not have access to references
At this point release is ready so Lucy needs to merge it into both master and develop branches. She starts with the master branch:
$ git checkout master Switched to branch 'master' $ git merge --no-ff release/release-1.0 Merge made by the 'recursive' strategy. README.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.txt $ git tag -a 1.0 $ git push Counting objects: 1, done. Writing objects: 100% (1/1), 227 bytes, done. Total 1 (delta 0), reused 0 (delta 0) remote: Processing changes: refs: 2, done To ssh://lucy@personal-centos:29418/gitflow-repo 2ecc701..ba4dfac HEAD -> master $ git push --tags Counting objects: 1, done. Writing objects: 100% (1/1), 159 bytes, done. Total 1 (delta 0), reused 0 (delta 0) remote: Processing changes: refs: 1, done To ssh://lucy@personal-centos:29418/gitflow-repo * [new tag] 1.0 -> 1.0
As you can see our release has just got merged into master. Next steps for Lucy will be to merge the changes back to develop branch and to delete remote branch release/release-1.0. We will leave those tasks as an exercise to the reader.
We are also going to skip the hotfix branches, as their workflow looks very much like that for release branches. The only thing to remember here is that in our setup only Release Managers or Admins are able to merge hotfixes back to master.
As you can see thanks to our configuration the developers and release managers can do their job while having extra security thanks to restricted permissions controlled by Gerrit. In the next blog post we will have a look on how the gitflow repo category that is used here was developed and how to create your own repository category in our Gerrit 2.6-based integration.