Deploy custom headless Sitecore solution in Sitecore XM Cloud

Previously, in the Running custom next.js editing host in Sitecore XM Cloud article, I wrote about running a custom editing host app in XM Cloud. In this article I want to share my experience deploying and running a custom headless Sitecore solution, which issues I faced and how I solved them.

Let’s define what I have at the beginning:

  • Sitecore 10.2 XM
  • JSS 19.0
  • SXA 10.2
  • JSS next.js rendering host app of version 19.0
  • Sitecore Content Serialization as a system for serializing, sharing, and deploying content items, as well as keeping them in version control.
  • Helix based .NET solution
  • Hosting in Azure Kubernetes Services

The main goal of all I will describe in this article is to measure the time that I need to move our project form AKS to Sitecore XM Cloud. Once I hadn’t had any experience with XM cloud before, I started from reading documentation. Finally, comparing a starting kit solution with what I have, I identified the following:

1. I need to update references for my project. Almost all Sitecore assemblies have to be replaced with Sitecore.XmCloud:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <PlatformVersion>1.*</PlatformVersion>
    <SitecoreVersion>10.2.0</SitecoreVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Update="Sitecore.Nexus" Version="$(SitecoreVersion)" />
    <PackageReference Update="Sitecore.XmCloud.Kernel" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.Mvc" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.ContentSearch" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.ContentSearch.Linq" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.ContentSearch.ContentExtraction" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.ExperienceForms.Client" Version="$(SitecoreVersion)" />
    <PackageReference Update="Sitecore.XmCloud.LayoutService" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.LayoutService.Mvc" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.Assemblies" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.XA.Foundation.Sitecore.XmCloudExtensions" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.Assemblies.SitecoreHeadlessServicesServer" Version="19.*" />
    <PackageReference Update="RichardSzalay.Helix.Publishing.WebRoot" Version="1.5.6" />
    <PackageReference Update="Sitecore.XmCloud.XA.Feature.Search" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.XA.Foundation.Abstractions" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.XA.Feature.SiteMetadata" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.Services.Core" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.XA.Foundation.Search" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.ExperienceForms.Mvc" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.JavaScriptServices.Configuration" Version="$(PlatformVersion)" />
    <PackageReference Update="Sitecore.XmCloud.JavaScriptServices.ViewEngine" Version="$(PlatformVersion)" />
    <PackageReference Update="Microsoft.AspNet.Mvc" Version="5.2.7" />
  </ItemGroup>
</Project>

}

 

I have replaced all assemblies that I have found a corresponding Sitecore.XmCloud assembly for. And I have updated the dependencies in .csproj files. But in general, I don’t think it is required to replace them because we don’t include any Sitecore libraries into the final package, but from my point of view it allows us to make sure that our core is compilable in XM Cloud. 

2. We have to upgrade the Sitecore Command Line Interface to version 5 and add  XM Cloud plugin:

Sitecore CLI

 

3. we need to add the xmcloud.build.json in the root of our solution. This article describes which parameters we have in the configuration: 

{
    "deployItems": {
        "modules": [
            "Foundation.*",
            "Feature.*",
            "Project.*"
        ]
    },
    "buildTargets": [
      "./DemoCloud.sln"
    ],
    "renderingHosts": {
        "global": {
            "path": "./src/Project/Demo/nextjs",
            "nodeVersion": "14.15.3",
            "jssDeploymentSecret":"dd5d764c4776459e92e1233b8cde0ab5",
            "enabled": true,
            "type": "sxa"
        }
    }
}
 

Where,

  • deployItems - Allows you to define what serialization item modules you want to deploy. It is similar to the serialization configuration defined in the sitecore.json file. Useful to limit what you deploy when working locally.
  • buildTargets - Allows you to define which projects and solutions to build.
  • renderingHosts - Allows you to configure the location for the rendering hosts and the sites they map to. I wrote about this parameter in more detail in this article.

I would like to pay attention to buildTargets parameter because it, actually, defines what will be built. In our solution we were using a “build” project. It is a project which referrer all Helix based projects (Foundations, Features and Projects) and publish them to the target folder. Even if we build a solution file, it raises a build of this project and we get all libraries published into the target. It works perfectly with docker build but, unfortunately, it doesn’t work with XM Cloud at all. Of course, we can define each project in the buildTargets array, but it is something that I definitely don’t want to do. 

I found another way to solve the issue. Maybe it is not the best solution, but it works for me. 

  • I have refactored all .csproj files to enable auto publish on build:
<!-- Begin Publish on Build -->
  <PropertyGroup>
    <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
    <PublishProfile>Local</PublishProfile>
  </PropertyGroup>
  <PropertyGroup>
    <AutoPublish Condition="'$(AutoPublish)' == '' and '$(Configuration)' == 'Debug' and '$(BuildingInsideVisualStudio)' == 'true' and '$(PublishProfile)' != ''">true</AutoPublish>
    <AutoPublishDependsOn Condition="'$(AutoPublish)' == 'true'">
      $(AutoPublishDependsOn);
      WebPublish
    </AutoPublishDependsOn>
  </PropertyGroup>
  <Target Name="AutoPublish" AfterTargets="Build" DependsOnTargets="$(AutoPublishDependsOn)">
  </Target>
  <!-- End Publish on Build -->

 

  • I have added Local.pubxml to the Properties\PublishProfiles\ with the following content for local testing purposes. This pablish profile will be overridden by XM Cloud anyway:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <WebPublishMethod>FileSystem</WebPublishMethod>
    <PublishProvider>FileSystem</PublishProvider>
    <LastUsedBuildConfiguration>Debug</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish />
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <publishUrl>..\..\..\..\docker\deploy\platform</publishUrl>
    <DeleteExistingFiles>False</DeleteExistingFiles>
  </PropertyGroup>
</Project>

 

  • I have added for each project {PROJECT-NAME}.wpp.targets file specify what need to be included into the target folder:
 <?xml version="1.0" encoding="utf-8"?>
<!--
  A .wpp.targets file can be used to configure web application publishing.
  https://docs.microsoft.com/en-us/aspnet/web-forms/overview/deployment/advanced-enterprise-web-deployment/excluding-files-and-folders-from-deployment
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!--
    Use the SitecoreAssemblies ItemGroup provided by the Sitecore.Assemblies.Platform
    package in order to prevent built-in Sitecore assemblies from publishing.
    https://doc.sitecore.com/developers/93/sitecore-experience-manager/en/sitecore-assembly-list-nuget-packages.html

    Delay executing this target until after Compile in order to
    ensure the NuGet package targets have been evaluated.
  -->
  <Target Name="ExcludeSitecoreAssemblies" AfterTargets="Compile">
    <ItemGroup>
      <ExcludeFromPackageFiles Include="@(SitecoreAssemblies -> 'bin\%(Filename)%(Extension)')" />
      <ExcludeFromPackageFiles Include="@(SitecoreAssemblies -> 'bin\%(Filename).pdb')" />
      <ExcludeFromPackageFiles Include="@(SitecoreAssemblies -> 'bin\%(Filename).xml')" />
    </ItemGroup>
  </Target>
</Project>

 

To make the exclusions above working, your project should have a reference to the Sitecore.XmCloud.Assemblies assembly. 


When I finished all the refactoring above, I was able to successfully run my project in XM Cloud. For my small project it took around 2 hours which I think was a pretty good result.