Continuous deployment met Umbraco

Rick van Schalkwijk

Continuous deployment is bij veel grote ontwikkelpartijen een belangrijk onderwerp van gesprek. Hoe kunnen we de uitrol van applicaties zo snel en gecontroleerd mogelijk laten verlopen? Op welke manier kunnen we het voor developers makkelijker maken hun websites snel en over meerdere systemen te deployen?

Op dit moment verloopt het beheren van de Umbraco content binnen mijn Team handmatig. Er treden soms bugs op tijdens het invoeren van content binnen het CMS. Daarnaast is het tijdrovend om tijdens elke stap binnen het continuous deployment proces content via de backoffice van het CMS in te voeren. Voor dit probleem is er binnen het multi client team een applicatie / toolset ontwikkeld die de content van een CMS kan synchroniseren binnen de verschillende stappen van het continuous deployment proces.

Zodra er een Umbraco instantie, van bijvoorbeeld de development server naar test gedeployed wordt, deze stap is natuurlijk volledig geautomatiseerd met behulp van Atlassian Bamboo, loopt de content niet meer gelijk. Met “de content” wordt bedoeld de verschillende configuraties die door een developer gedaan zijn aan de backend/backoffice van de desbetreffende Umbraco instantie. Een voorbeeld hiervan zijn de zogenoemde “document types”. Met behulp van document types kan een developer structuur aanbrengen in de content die een redacteur moet invoeren. Binnen een document type kunnen verschillende properties worden gedefinieerd zoals welke content elementen kunnen er binnen een pagina worden gebruikt: een richtext editor, dropdown fields, etc. Daarnaast heeft een document type zelf ook nog de nodige eigenschappen: naam, alias, template en een positie binnen de zogenoemde tree structure. Deze document types moeten tijdens elke deployment met de hand opnieuw ingevoerd worden op een andere server. Ook heeft niemand binnen het team zicht op waar, wanneer en wie document types toevoegt aan het CMS.

UTransporter

Het bovenstaande kreeg ik te horen toen ik begin 2014 begon met mijn afstudeerstage bij één van de teams van Mirabeau. Dit was een probleem waar het team veel uren aan kwijt raakten. Aan mij was de vraag om hier een oplossing voor te ontwikkelen, of onderzoek te doen naar bestaande oplossingen. Uiteindelijk is gekozen om zelf een applicatie te ontwikkelen genaamd “UTransporter”. De werking van deze applicatie is als volgt: ontwikkelaars kunnen UTransporter via de NuGet package manager toevoegen aan hun Umbraco solution. Vanaf dat moment komen er in de backend van Umbraco een aantal extra optie beschikbaar: Sync, Dryrun, Generate en Show logging. Te zien in onderstaande figuur.

Continuous-deployment-met-Umbraco-Sync-Backend-plugin-screenshot

Functionaliteiten

Dit zijn de verschillende acties die een developer kan nemen om zijn document types te beheren. Met “Sync” worden alle document types met hun properties, tabs en datatypes toegevoegd aan de huidige Umbraco instantie. Het framework heeft met een reden de naam “Umbraco Sync”, het kan namelijk ook overweg met verschillende mutaties op document types, document type properties en datatypes. Zodra er een verandering plaats vindt binnen een document type class, dit kan zijn aan eigenschappen van het document type zelf of aan de properties die bestaan binnen een document type, zal het framework de gepaste actie ondernemen. Tijdens het draaien van de synchronisatie zal het framework als eerste kijken of het document type al bestaat, is dit niet het geval dan zal het direct worden toegevoegd.Vervolgens zullen de gepaste mutaties worden uitgevoerd. Met de optie “Generate” worden alle document types die in het Umbraco CMS voorkomen maar nog niet op files bestaan gegenereerd. Dit is zeer handig als Umbraco Sync ingezet wordt binnen een project waar al langere tijd aan gewerkt is. Sommige lopende projecten binnen het MCT hebben tientallen document types gedefinieerd in de backend. Deze kunnen dan met “een druk op de knop” gegenereerd worden en hoeven dus niet handmatig door developer te worden overgetypt. Voor het genereren van de document type classes is gekozen voor “CodeDOM”. CodeDOM is een ingebouwde feature van het Microsoft .NET Framework en is bedoeld om C# classes te generen, dit zijn classes met de .cs extensie. Door het gebruik van CodeDOM kan op een veilige en robuuste manier data van de database naar een file worden geschreven. Naast het feit dat Code-DOM veilig en robuust is biedt het ondersteuning voor bijna alle C# taal specifieke elementen zoals: “custom attributes”, “usings” en zeer belangrijk “C bracing style”. Deze taal specifieke elementen, met namen custom attributes, worden volop gebruikt binnen de document type classes.

Als laatste is een “Dry Run” feature ontwikkeld. Met behulp van deze functie kan een developer alle verschillende mutaties bekijken zonder de acties uit te voeren. Op deze manier kunnen errors en ongewensten mutaties afgevangen worden. De dry run feature maakt met behulp van .NET SQL Managment Object een kopie van de huidige database, deze krijgt een hard coded prefix. Als het aanmaken van de database en kopiëren van alle data goed verlopen is zal de applicatie verbinding maken met deze database om vervolgens hierop een “proef” synchronisatie te draaien. Zodra de dry run klaar is wordt de kopie database verwijderd en kan de developer de logs bekijken en vervolgens besluiten de synchronisatie uit te voeren op de “echte” database. Op deze manier kan er garantie worden gegeven dat er geen data corrupt raakt tijdens een synchronisatie.

Document types, zoals die in de derde paragraaf beschreven staan, worden geëxporteerd naar C# classes. Op deze manier kunnen document type mee de Git repository in en door de complete OTAP straat heen. Hieronder een voorbeeld van zo’n C# class.

namespace DocumentTypes
{
    using System;
    using Mirabeau.Umbraco.Synctool.Attributes;
    using Mirabeau.Umbraco.Synctool.Enums;
    using Mirabeau.Umbraco.Synctool.Models;

    [DocumentType(Name="QuoteContainer", Alias="Quotecontainer", Icon=".sprTreeFolder", Thumbnail="folder.png", Description="", AllowAtRoot=false, DefaultTemplate=typeof(Templates.Articlepage), AllowedTemplates=new System.Type[] { typeof(Templates.Articlepage)}, AllowedChildNodeTypes=new System.Type[] { typeof(DocumentTypes.Homepage), typeof(DocumentTypes.Data)})]
    public class QuoteContainer : Data
    {
        private string _PropertyOneFromContainer;

        [DocumentTypeProperty(UmbracoPropertyType.Textstring, Name="PropertyOneFromContainer", Alias="propertyonefromcontainer", Description="", SortOrder=0, Mandatory=false, ValidationRegExp="")]
        public string PropertyOneFromContainer
        {
            get
            {
                returnthis._PropertyOneFromContainer;
            }
            set
            {
                this._PropertyOneFromContainer = value;
            }
        }
    }
} 

De class representeert het document type, op deze class zijn een aantal standaard custom attributes te vinden. Dit zijn eigenschappen die normaal gesproken via de backend moeten worden ingevoerd. Ook zien we de method "PropertyOneFromContainer" binnen de class en ook deze heeft een aantal standaard custom attributes. De method binnen zo'n class geeft aan welke eigenschapen een document type mag hebben, in dit geval is het een "TextString" of terwijl een input field. In onderstaande figuur zien we de backend van Umbraco met het document dat in het bovenstaande stuk code gedefinieerd is.

Continuous-deployment-met-Umbraco-documenttype-screenshot

Zodra er een een nieuwe applicatie wordt gereleaset naar bijvoorbeeld test, worden alle document type die in code zijn gedefinieerd gedeployed naar de desbetreffende server. Vervolgens wordt er via de backend van Umbraco de gewenste actie uitgevoerd: sync, export, dry run, etc. UTransporter begint met het uitlezen van alle bestanden, binnen de applicatie, opzoek naar document type classes. Deze worden herkend door dat ze een implementatie zijn van de “DocumentType” class. Daarna worden de attributes uitgelezen met behulp van “Reflection”. Als laatste worden deze vergeleken met de data die al aanwezig is binnen de huidige database. De verschillen worden recht getrokken en beide instanties lopen qua document types gelijk. Dit proces wordt in onderstaande figuur schematisch weergegeven.

Continuous-deployment-met-Umbraco-Sync-tla

Wat hier opvalt, is dat de gemaakte applicatie niet rechtstreeks tegen de database aanzit. Er is gekozen om gebruik te maken van de Umbraco API die standaard achter het CMS zit. Op deze manier wordt op een robuuste en betrouwbare manier met data omgegaan.

Waarom is er zelf een applicatie ontwikkeld?

Waarom is er binnen ons team gekozen om zelf een applicatie te ontwikkelen i.p.v. een bestaande oplossing te gebruiken, zoals bijvoorbeeld Courier 2. Ten eerste hadden we op deze manier volledige controle over de code-base. Denk hierbij aan code quality, coverage en distributie. Ten tweede heeft dit ons de vrijheid gegeven om verschillende implementaties te bouwen die specifiek gericht zijn op onze OTAP straat. We kunnen de applicatie koppelen aan verschillende voorkanten, bijvoorbeeld een backend plugin gemaakt met angularJS voor Umbraco 7. Als derde en in mijn ogen belangrijkste verschil is dat UTransporter de document types exporteert naar "strongly typed" C# classes. Dit betekend dat als er verkeerde data wordt ingevoerd door een developer de applicatie/website een compile error geeft. Op deze manier kunnen we veel voorkomende fouten direct afgevangen. Dit hebben we gedaan met custom attributes, de attibuten die te vinden zijn op een document type class. Als laatste is het natuurlijk geweldig om een project dat je hebt ontwikkeld te mogen open-sourcen als bedrijf.

Conclusie

Met behulp van “UTransporter” kunnen we binnen ons team veel tijd besparen als het aankomt op het uitvoeren van een release. Met behulp van deze tool kunnen wij binnen tijdbestek van tien minuten een productie/acceptatie release doen. Daarnaast is het release proces, voor alle omgevingen, minder foutgevoelig omdat data niet meer handmatig hoeft worden ingevoerd. Het het gehele UTransporter project is via GitHub beschikbaar gesteld onder de BSD 3-Clause licentie. Dit betekend dat andere ontwikkelaars en bedrijven deze tool ook in gebruik kunnen nemen. Hij is ook te vinden op NuGet