Showing posts with label Install packages. Show all posts
Showing posts with label Install packages. Show all posts

27 November 2014

Sitecore 7.5 and Installing Packages through handler is broken


I was upgrading a 7.2 to 7.5 today and I noticed something unexpected that I wanted to share today.

As maybe a few of you, I am using TDS to generate my update packages for my deployments. This is awesome and We have a .ashx on our QA environment that automatically install those packages and publish items afterwards. Well, this was quite nice and working fine on Sitecore 7.2.

However, after upgrading the site to 7.5 I noticed that the solution was not compiling against the new Sitecore DLL.. And the reason was: SaveInstallationMessages() method was removed from the UpdateHelper on the Sitecore 7.5. So the following line was failing:

                    UpdateHelper.SaveInstallationMessages(entries, text);


If you compare the previous version of the Install() method from Sitecore.Update.InstallUpdatePackage

protected string Install()
{
 string result;
 using (new ShutdownGuard())
 {
  this.logEntries = new List();
  PackageInstallationInfo installationInfo = this.GetInstallationInfo();
  string text = null;
  List entries = null;
  try
  {
   this.WriteMessage(string.Format("{0} package: {1}", (installationInfo.Action == UpgradeAction.Preview) ? "Analyzing" : "Installing", installationInfo.Path), null, Level.INFO, false);
   entries = UpdateHelper.Install(installationInfo, this, out text);
  }
  catch (PostStepInstallerException ex)
  {
   entries = ex.Entries;
   text = ex.HistoryPath;
   throw ex;
  }
  finally
  {
   UpdateHelper.SaveInstallationMessages(entries, text);
  }
  result = text;
 }
 return result;
}

With the new version:

// Sitecore.Update.InstallUpdatePackage
protected string Install()
{
 string result;
 using (new ShutdownGuard())
 {
  this.logEntries = new List();
  PackageInstallationInfo installationInfo = this.GetInstallationInfo();
  string text = null;
  this.logMessages = new List();
  try
  {
   this.WriteMessage(string.Format("{0} package: {1}", (installationInfo.Action == UpgradeAction.Preview) ? "Analyzing" : "Installing", installationInfo.Path), null, Level.INFO, false);
   this.logMessages = UpdateHelper.Install(installationInfo, this, out text);
   base.InstallationHistoryRoot = text;
  }
  catch (PostStepInstallerException ex)
  {
   this.logMessages = ex.Entries;
   base.InstallationHistoryRoot = ex.HistoryPath;
   throw ex;
  }
  finally
  {
   this.SaveInstallationMessages();
  }
  result = text;
 }
 return result;
}

You can notice the SaveInstallationMessages is now defined on the control itself. This is a bit annoying as we cant re-use it in our handler anymore.
Since the UpdateHelper.Install is still outing the history path I could go around the issue by adding the SaveInstallationMessages() into our handler directly but that means duplication of methods. I would have prefered the old static method, but I could not find any other way for now...

        protected string Install(string package)
        {
            var log = LogManager.GetLogger("LogFileAppender");
            string result;
            using (new ShutdownGuard())
            {
                var installationInfo = new PackageInstallationInfo
                {
                    Action = UpgradeAction.Upgrade,
                    Mode = InstallMode.Install,
                    Path = package
                };
                string text = null;
                List entries = null;
                try
                {
                    entries = UpdateHelper.Install(installationInfo, log, out text);
                }
                catch (PostStepInstallerException ex)
                {
                    entries = ex.Entries;
                    text = ex.HistoryPath;
                    SC.Diagnostics.Log.Error("Automated Deployment error " + ex.StackTrace, "Automated deployment");
                    throw;
                }
                finally
                {
                    this.SaveInstallationMessages(entries, text);
                }

                result = text;
            }

            return result;
        }

        public string SaveInstallationMessages(System.Collections.Generic.List entries, string historyPath)
        {
            string text = System.IO.Path.Combine(historyPath, "messages.xml");
            FileUtil.EnsureFolder(text);
            using (System.IO.FileStream fileStream = System.IO.File.Create(text))
            {
                XmlEntrySerializer xmlEntrySerializer = new XmlEntrySerializer();
                xmlEntrySerializer.Serialize(entries, fileStream);
            }
            return text;
        }

3 May 2014

Backup using serialization before deploying packages

Well in previous post, we saw that we could generate the serialization prior installing any packages. Well the underlying goal was to actually backup the content... So let's extend a bit and see if we can do a bit more. What would be nice would be to delete the entire folder prior serialization and zipping the serialization folder once it is completed...

1- Get the serialization folder:
This is quite simple and you should be able to do it with the following code:
            string SerializationRootFolder = FileUtil.MapPath(SC.Configuration.Settings.SerializationFolder);


2- Clear the folder
So let's delete all files and subfolders... Obviously you will only be able to do that if you have the required file permission setup... Check your app pool if using network services... But for this exercies, let's say you have setup the permissions so you are allowed to delete files and folder...
        /// 
        /// Clear Serialisation folder
        /// 
        /// 
        private void ClearSerializationFolder(string SerializationRootFolder)
        {
            System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(SerializationRootFolder);

            foreach (FileInfo file in downloadedMessageInfo.GetFiles())
            {
                file.Delete();
            }
            foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories())
            {
                dir.Delete(true);
            }
        }

3- Start serializing the entire content tree

Well guess what, we do have this code from previous post
        private void BackupItemTree(string id)
        {
            Database db = SC.Configuration.Factory.GetDatabase("master");
            Item itm = db.GetItem(id);

            SC.Data.Serialization.Manager.DumpTree(itm);
        }

4- Zipping it
To zip the files, you can use different libraries available for .NET, but since Sitecore has a Zip library, why not using it...
        /// 
        /// Create the zip file of serialisation
        /// 
        /// 
        /// 
        private string CreateZipFile(string SerializationRootFolder)
        {
            string backupName = string.Format("backup_serialization_{0}.zip", DateTime.Now.ToString("yyyyMMddhhmmss"));

            var zipFile = Path.Combine(SerializationRootFolder, backupName);

            using (var fileWriter = new SC.Zip.ZipWriter(zipFile))
            {
                var files = GetAllFiles(SerializationRootFolder, SearchOption.AllDirectories);

                SC.Diagnostics.Log.Info(string.Format("Adding files ({0})", files.Count()), this);

                var length = SerializationRootFolder.Length;
                if (!SerializationRootFolder.EndsWith("\\"))
                {
                    length += 1;
                }

                foreach (var file in files)
                {
                    fileWriter.AddEntry(file.Remove(0, length), file);
                }
            }

            return zipFile;
        }

        /// 
        /// Get all files - you can include all subdirectories through search options
        /// 
        /// 
        /// 
        /// 
        protected string[] GetAllFiles(string path, SearchOption searchOption)
        {
            List files = new List();

            string searchPattern = "*.item";
            files.AddRange(Directory.GetFiles(path, searchPattern, searchOption));
            
            return files.ToArray();
        } 


OK, time to test that... SO let's install a package and watch the serialization folder:



29 March 2014

Package Installation


Packages, Packages...
A colleague of mine ask me a question about installing packages today. And I wanted to post about the different options available when installing packages. The source of the information describe are from a great post by Martijn Van Der Put.

So when you are installing a package you will have the following screen - if the package is installing items present in the current content tree:


Well the best description for those options are (Thanks Martjin):

Overwrite  Items with the same ID (along with it's descendants) will be removed and replaced by items from the package. 
Skip Items with the same ID from the target database will remain unchanged; the item from the package will be skipped.
Merge - Clear all existing versions for a language of the item being installed are removed prior to adding new versions. This options 'clears out' the versions of the language and creates one new version.
Merge - Append item versions from the package are added 'on-top' of the existing versions of the item. This preserves history, but numbers the package versions with numbers higher than the existing version numbers. A user can merge information between versions afterwards.
Merge - Merge if there is a version with the same number in the item, the Installation Wizard will overwrite it, otherwise a new version with the specific number is added. This makes it possible to replace specific versions of items.


So the important thing here is: If you create a package with the homepage only to do some update and you select overwritte then your entire content tree will be deleted...

10 March 2014

Install .update package through an ASHX

Well, today I played a bit with TDS, adding configuration for our Internal QA environment, UAT and Prod (Authoring and Delivery). And this got me thinking on how I could install those packages automatically without having to go through the /sitecore/admin/updateinstallationwizard.aspx

So what I wanted to do is:
  • Create an ashx that will install my packages
  • Create a schedule tasks in sitecore, so I don't even have to trigger this ashx 
Awesome, let's get started

1- The Handler

So the idea is to dump the update packages on a folder. the scheduled task will watch this folder and install the packages when any... So for this exercise we will just say that the folder to watch will be:
/sitecore/admin/Automated_Packages

So let's create our ashx.

the first thing you want to do is to get the list of files on the specific folder:

            var files = Directory.GetFiles(Sitecore.MainUtil.MapPath("/sitecore/admin/Automated_Packages"), "*.update", SearchOption.AllDirectories);


The next step will be to Install the different packages when found. So inside the Foreach loop going through all the files you can execute the following code. This shows you how to use the UpdateHelper to install the package file:

        protected string Install(string package)
        {
            var log = LogManager.GetLogger("LogFileAppender");
            string result;
            using (new ShutdownGuard())
            {
                var installationInfo = new PackageInstallationInfo
                {
                    Action = UpgradeAction.Upgrade,
                    Mode = InstallMode.Install,
                    Path = package
                };
                string text = null;
                List entries = null;
                try
                {
                    entries = UpdateHelper.Install(installationInfo, log, out text);
                }
                catch (PostStepInstallerException ex)
                {
                    entries = ex.Entries;
                    text = ex.HistoryPath;
                    Sitecore.Diagnostics.Log.Error("Deployment error " + ex.StackTrace, "Automated deployment");
                    throw;
                }
                finally
                {
                    UpdateHelper.SaveInstallationMessages(entries, text);
                }

                result = text;
            }

            return result;
        }

After your packages have been installed, what you may want to do is to actually publish your changes to make sure all gets pushed to the delivery server. As our packages usually include only templates, layout, renderings, and some System items we could use something like the following to publish our content - the other way is to republish the entire site - I put both in the same method so you can choose which one is more suitable.
        protected static void Publish()
        {
            Sitecore.Context.SetActiveSite("shell");
            using (new SecurityDisabler())
            {
                DateTime publishDate = DateTime.Now;
                Sitecore.Data.Database master = Sitecore.Configuration.Factory.GetDatabase("master");
                Sitecore.Data.Database web = Sitecore.Configuration.Factory.GetDatabase("web");

                // publish specific section - Need to  put those in settings
                ID templateFolderID = new ID("{3C1715FE-6A13-4FCF-845F-DE308BA9741D}");
                ID layoutFolderID = new ID("{EB2E4FFD-2761-4653-B052-26A64D385227}");
                ID systemFolderID = new ID("{13D6D6C6-C50B-4BBD-B331-2B04F1A58F21}");

                PublishManager.PublishItem(master.GetItem(templateFolderID), new Database[] { web }, LanguageManager.GetLanguages(master).ToArray(),true,false);
                PublishManager.PublishItem(master.GetItem(layoutFolderID), new Database[] { web }, LanguageManager.GetLanguages(master).ToArray(), true, false);
                PublishManager.PublishItem(master.GetItem(systemFolderID), new Database[] { web }, LanguageManager.GetLanguages(master).ToArray(), true, false);
                
                // republish entire site - not sure if we want granular publish or full republish
                // PublishManager.Republish(Sitecore.Client.ContentDatabase, new Database[] { web }, LanguageManager.GetLanguages(master).ToArray(), Sitecore.Context.Language);
            }
        }


19 February 2014

Using TDS


I wanted to do a quick post about TDS here as I am asked quite often: what is the TDS project in your solution? What is TDS? What can you do with it...
  
In most agencies you will work on projects where the team is not working in a common location. As most of you know TDS is a fantastic tools to be able to sync your databases with other team members. If like us you are working with an offshore model then TDS is awesome: you can keep track on template changes and have all of it through Visual Studio and in Version control. As expected quite a lot of Sitecore people knows about the tool and are not surprised. However, the surprise came when I am adding that I am using TDS to create my .update packages (files and Sitecore Items) for deployment into Internal QA then UAT then Prod. The package install could be automated or manual process, but that is off topic for this post. So it is only then that I realize that some of us did not use this feature from TDS - and maybe I should talk about how to use TDS for Synching content but also generate the Update Packages...

1- Synchronise content:

That is quite simple to do. Once you have created you project in Visual studio, you just need to configure the "Build" Tab to make sure TDS will point at your website. Please refer to the TDS documentation on how to create the project in Visual Studio...


Once this is setup then you will be able to use the different option from the content tree to:
  • Get Item from Sitecore
  • Synchronsie with Sitecore


When Synchronising with Sitecore you will have the following screen where you will be able to either update your sitecore instance or update the Solution file.


That will allow you to work on your task then update the solution then checkin + get the latest work from someone else and update your sitecore instance. That is awesome: all templates, layouts... are now in TDS

2- Update Packages

Now the fun starts:
You have been working on your task, as well as other developers on different area of your application. Everything looks good, everyone has been using TDS correctly and checking in their code and Sitecore items... Well it is time to deploy to TEST. I am skipping the debate of automated or manual process for later. So for the sake of this post Let say I will deploy manually. So I am pretty sure as most of us did it in the past: open the desktop > create package > select what we need... Well if you were working with TDS you could configure the TEST "configuration" so instead of connecting to your test site, yu want to create the update package:

  • Right click on your TDS project and select properties
  • Then switch the Configuration to TEST as per the below:

  • Note that the Build Tab is left empty.
  • Go to the Update Package tab, and start filling in the information for your package:

  • Once setup, if you switch the configuration profile in Visual Studio and Right click on any Sitecore Item in TDS, you will note that none of the "Get Sitecore Item" "Sync with Sitecore" are available. This is because you do not want to connect to a sitecore instance, you just want to generate a package


  •  Now, I can hear you say: Hold on, what will be included in my package? Well the answer is quite simple: what you want to be included. For that, you will need to right click on your TDS project and select "Deployment Property Manager".

This will open up the pane as per the following screen, where you can decide what to include. As a preference, I usually include all my templates, all my layout and renderings but None of the Content Item to make sure those does not get overwritten... Then again, from time to time, I do include some items with the "deploy" property "Once" instead of always. This is to make sure that this item will be created if it does not exist but nothing will happen if it does...


Well the last step is simple: build your TDS project using the TEST profile and ... voila:
You have your update packages on the TDS project (file system...):
  
Once those packages are generated, you can install them manually through the Sitecore update installer: http://mysite/sitecore/admin/updateinstallationwizard.aspx

Or you can look for automate the deployment...

NOTE: There is one thing I did not talked abut on this post is the Config replacement, which I am planning on talking in a later post