Usually, during the standard process of development, you would have several environments like DEV, QA, UAT/STAGING, and so on. The more environments you have - the more cases and differences in deployment you will need to implement. In this post, I would try to describe how you could simplify your life implementing CI for Sitecore based projects.

Solution

Almost immediately you would face a question of content synchronizations. There are a lot of options that you could use:

  • Content Package - the most obvious variant one could come up with however you cannot automate it out of the box. Automation of content packages deployment is quite a complex task as you need to invent versioning for them, need to store in them in VCS.
  • Unicorn project - solution based on content serialization. You will need to integrate some event handlers on content changes to keep the Sitecore serialization folder up to date. It will allow you to update content via HTTP call from CI as well.
  • Hedgehog TDS - this is an extension to Visual Studio that does a lot more than just serialization. You could merge content, sync from VS generate code based on templates, and so on. With CI it could be integrated quite easily as you just need to build a project with a specific setting.
  • Database project - more or less standard variant when it came to DB sync, but with Sitecore, it would not work as well as expected. You will not have any control of sync of Sitecore items and after a while, it will be an extremely long operation especially if you have some media items in DB. From my practice TDS - is the most useful tool, but it is not free so you need to decide for yourself.

Moreover, when it comes to users and role synchronization - none of these tools will, except the DB project (but I would not recommend it due to the reasons above). To solve this issue I’ve come up with several pipelines that could be started on Sitecore init and check roles needed or even create them. The most suitable place for this operation is initialize pipeline. Processor implementation for this pipeline is quite simple - we just check and create roles if they do not exist (see the code below).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
namespace SampleSite
{
    using System.Linq;
    using System.Web.Security;
 
    using Sitecore.Diagnostics;
    using Sitecore.Pipelines;
    using Sitecore.Security.Accounts;
 
    public class RolesInitializer
    {
        public const string ContentContentEditorRole = @"sitecore\sample_content_editor";
 
        private const string SitecoreAuthorRole = @"sitecore\Author";
 
        private const string SitecoreClientPublishingRole = @"sitecore\Sitecore Client Publishing";
 
        private const string SitecoreDesignerRole = @"sitecore\Designer";
 
        public virtual void Process(PipelineArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
 
            Assert.IsTrue(Role.Exists(SitecoreAuthorRole), SitecoreAuthorRole + " is missing");
            Assert.IsTrue(Role.Exists(SitecoreDesignerRole), SitecoreDesignerRole + " is missing");
            Assert.IsTrue(Role.Exists(SitecoreClientPublishingRole), SitecoreClientPublishingRole + " is missing");
 
            this.CreateRole(ContentContentEditorRole, SitecoreDesignerRole, SitecoreAuthorRole, SitecoreClientPublishingRole);
        }
 
        protected virtual void CreateRole(string name, params string[] args)
        {
            // check & create content_editor role
            if (!Role.Exists(name))
            {
                Roles.CreateRole(name);
            }
 
            if (args != null && args.Length > 0)
            {
                foreach (var s in args.Where(s => !RolesInRolesManager.IsRoleInRole(Role.FromName(name), Role.FromName(s), false)))
                {
                    RolesInRolesManager.AddRoleToRole(Role.FromName(name), Role.FromName(s));
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
<pipelines>
  <initialize>
    ...
    <processor type="SampleSite.RolesInitializer, SampleSite">
    <processor type="SampleSite.UsersInitializer, SampleSite">
    ...
  </initialize>
</pipelines>

Users' creation is a little bit more complex and requires different configurations for each environment. To do this you will need transforms to include configurations. I will describe tools and approaches in the following articles.