3

How do I build the front end static assets for a SXA web site in my build pipeline, and then import into Sitecore in the release pipeline ?

I would like to run unit tests, code scanning, and produce code quality reports as part of my build process, and using the build pipeline as one the gatekeeper to prevent rogue front end code to be released.

4

According to the documentation, https://doc.sitecore.com/developers/sxa/17/sitecore-experience-accelerator/en/trigger-creative-exchange-from-your-ci-server.html, as of SXA 1.7 there is support to import front end assets into Sitecore.

  1. First create a user with full admin access that is used to run SPE RestfulV2 services, then patch as follows
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:env="http://www.sitecore.net/xmlconfig/env/"  xmlns:role="http://www.sitecore.net/xmlconfig/role/">
    <sitecore>
        <powershell>
            <services>
                <restfulv2 role:require="ContentManagement or Standalone">
                    <patch:attribute name="enabled">true</patch:attribute>
                    <authorization>
                        <add Permission="Allow" IdentityType="User" Identity="sitecore\CI.User" />
                    </authorization>
                </restfulv2>
            </services>
        </powershell>
    </sitecore>
</configuration>
  1. Then create a Powershell script in your solution to trigger SPE scripts inside Sitecore externally. For SPE 5.0
<#
.SYNOPSIS

This script triggers any SPE scripts that are available to be used through the Restful V2 service. Sitecore Powershell Extensions 5.0 is required. Please use RestfulV2-BasicAuth.ps1 instead if Sitecore Powershell Extensions 5.1 or above is installed in the Sitecore Instance.

.DESCRIPTION

This script triggers any SPE scripts that are available to be used through the Restful V2 service. Sitecore Powershell Extensions 5.0 is required. Please use RestfulV2-BasicAuth.ps1 instead if Sitecore Powershell Extensions 5.1 or above is installed in the Sitecore Instance. This can be used on developer PC's, as well as during CI / CD process. RestfulV2 must be enabled in Sitecore Powershell Extensions for this to work. https://doc.sitecorepowershell.com/security
For Sitecore 9.1 and above that uses the Identity Server, Cognified.PowerShell.IdentityServer.config must be enabled. This config patch is available from SPE 5.1 and above.

.PARAMETER Username
The Sitecore username. Uses sitecore\admin by default.

.PARAMETER Password
The Sitecore user's password. Uses 'b' by default, because 'b' is cool.

.PARAMETER SitecoreHost
The Content Management Sitecore Host. eg. https://sc911xc91xp0.dev.local

.PARAMETER ScriptSitecoreHost
The SPE script located within /sitecore/system/Modules/PowerShell/Script Library

.PARAMETER Parameters
Powershell parameters to be executed against

.INPUTS

None.

.OUTPUTS

None.

.EXAMPLE

PS> .\RestfulV2-QueryStringAuth.ps1 -SitecoreHost https://sc911xc91xp0.dev.local -ScriptSitecorePath /ce-import -Parameters offset=3&limit=2&fields=(Name,ItemPath,Id)

#>

param(
    [string] $Username = "sitecore\admin",
    [string] $Password = "b",
    [Parameter(Mandatory=$true)]
    [string] $SitecoreHost,
    [Parameter(Mandatory=$true)]
    [string] $ScriptSitecorePath,
    [string] $Parameters
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = ("$SitecoreHost/-/script/v2/master{0}?user={1}&password={2}&{3}" -f $ScriptSitecorePath, $Username, $Password, $Parameters)
Write-Host $url
Invoke-WebRequest -URI $url

For SPE 5.1 and newer

<#
.SYNOPSIS

This script triggers any SPE scripts that are available to be used through the Restful V2 service. Sitecore Powershell Extensions 5.1 or above is required.

.DESCRIPTION

This script triggers any SPE scripts that are available to be used through the Restful V2 service. Sitecore Powershell Extensions 5.1 or above is required. This can be used on developer PC's, as well as during CI / CD process. RestfulV2 must be enabled in Sitecore Powershell Extensions for this to work. https://doc.sitecorepowershell.com/security
For Sitecore 9.1 and above that uses the Identity Server, Cognified.PowerShell.IdentityServer.config must be enabled. This config patch is available from SPE 5.1 and above.

.PARAMETER Username
The Sitecore username. Uses sitecore\admin by default.

.PARAMETER Password
The Sitecore user's password. Uses 'b' by default, because 'b' is cool.

.PARAMETER SitecoreHost
The Content Management Sitecore Host. eg. https://sc911xc91xp0.dev.local

.PARAMETER ScriptSitecoreHost
The SPE script located within /sitecore/system/Modules/PowerShell/Script Library, add query string parameters as the Powershell parameters

.INPUTS

None.

.OUTPUTS

None.

.EXAMPLE

PS> .\RestfulV2-BasicAuth.ps1 -SitecoreHost https://sc911xc91xp0.dev.local -ScriptSitecorePath /ce-import?offset=3&limit=2&fields=(Name,ItemPath,Id)

#>

param(
    [string] $Username = "sitecore\admin",
    [string] $Password = "b",
    [Parameter(Mandatory=$true)]
    [string] $SitecoreHost,
    [Parameter(Mandatory=$true)]
    [string] $ScriptSitecorePath
)

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

$pair = "$($Username):$($Password)"

$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))

$basicAuthValue = "Basic $encodedCreds"

$headers = @{
    Authorization = $basicAuthValue
}

Invoke-RestMethod -Headers $headers -Uri "$SitecoreHost/-/script/v2/master$ScriptSitecorePath"
  1. Perform a full Creative Exchange Export with Agency drop as an option, and Export to Folder on server. Sample Creative Exchange Export Settings Dialog
  2. Clean up all the folders that are not required for the front end build produced assets, you should end up with something like below. The front end build should produce all the distributed files into a folder structure that can easily be dropped into App_Data/packages/CreativeExchange/FileStorage//-/media/Themes//An example of the theme structure for Habitat Home Platform
  3. Sitecore provides a sample Creative Exchange Import script located at /sitecore/system/Modules/PowerShell/Script Library/SXA/SXA — Creative Exchange/Web API/ce-import. In my opinion the sample script is nowhere near good enough. I have enhanced it
<#
.SYNOPSIS

Performs a Sitecore Creative Exchange Import from the files on disk

.DESCRIPTION

This script utilises the Sitecore Creative Exchange to import front end static assets into the media library. This gives the front end development team full flexibility to use the tools
they are needed to produce the final output. This is a Sitecore Powershell Extension script that should be stored where all the other Script Libraries are placed in Sitecore.

.PARAMETER scriptArguments.DeviceId
The Device ID. If left empty, it will use the Sitecore Default Device. Please note that a Full Sitecore ID with curly braces are expected.

.PARAMETER scriptArguments.SitePath
The full Sitecore Item path to the Site in SXA. eg. /sitecore/content/Habitat SXA Sites/Habitat Home

.INPUTS

None. You cannot pipe objects to this script.

.OUTPUTS

None. This script performs the Creative Exchange Import

.EXAMPLE

N/A. This Sitecore Powershell Extensions script is triggered using Sitecore Powershell Extensions Restful API v2.

#>

Import-Function Get-CreativeExchangeImportResponse

if ($scriptArguments.ContainsKey("SitePath"))
{
    if (-Not $scriptArguments.ContainsKey("DeviceId"))
    {
        # Defaults to the Default Device in Sitecore
        $deviceId = "{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}"
    }
    else {
        $deviceId = $scriptArguments.DeviceId
    }

    $database = Get-Database -Name "master"
    $siteItem = Get-Item -Path $scriptArguments.SitePath

    $themesFolder = $siteItem.ThemesFolder
    $pageDesignItem = Get-Item -Path "$($siteItem.ItemPath)/Presentation/Page Designs"

    $globalScriptsOptimizerItem = Get-Item -Path "master:/system/Settings/Foundation/Experience Accelerator/Theming/Optimiser/Scripts"
    $globalStylesOptimizerItem = Get-Item -Path "master:/system/Settings/Foundation/Experience Accelerator/Theming/Optimiser/Styles"

    if (-Not [string]::IsNullOrEmpty($themesFolder) -and $pageDesignItem -ne $null -and $globalScriptsOptimizerItem -ne $null -and $globalStylesOptimizerItem -ne $null)
    {
        $themesFolderItem = Get-Item -Path "master:" -ID $themesFolder

        # Check to see if asset optimizer is used for the site
        $globalStylesOptimizerEnum = Get-Item -Path master: -ID $globalStylesOptimizerItem.Fields["Mode"].Value
        $globalScriptsOptimizerEnum  = Get-Item -Path master: -ID $globalScriptsOptimizerItem.Fields["Mode"].Value
        $globalStylesOptimizerValue = $globalStylesOptimizerEnum.Fields["Value"].Value
        $globalScriptsOptimizerValue = $globalScriptsOptimizerEnum.Fields["Value"].Value


        $isStylesOptimizingEnabled = $pageDesignItem.Fields["Styles Optimizing Enabled"].Value
        $isScriptsOptimizingEnabled = $pageDesignItem.Fields["Scripts Optimizing Enabled"].Value

        if ($globalStylesOptimizerValue -eq "Disabled")
        {
            $isStylesOptimizingEnabled = "0"
        }
        elseif ([string]::IsNullOrEmpty($isStylesOptimizingEnabled))
        {
            $isStylesOptimizingEnabled = "1"
        }

        if ($globalScriptsOptimizerValue -eq "Disabled")
        {
            $isScriptsOptimizingEnabled = "0"
        }
        elseif ([string]::IsNullOrEmpty($isScriptsOptimizingEnabled ))
        {
            $isScriptsOptimizingEnabled = "1"
        }

        Write-Host "Begin: Cleaning up theme folders..."
        $ScriptsFolders = Get-ChildItem -ID $themesFolder -Recurse | Where-Object { $_.Name -eq 'Scripts' } | Get-ChildItem -ID $_.ID -Recurse | Where-Object { (($isScriptsOptimizingEnabled -eq "1" -and -not $_.Name.StartsWith("optimized")) -or ($isScriptsOptimizingEnabled -ne "1")) } | Remove-Item -Confirm:$false -Force -Recurse
        $StylesFolders = Get-ChildItem -ID $themesFolder -Recurse | Where-Object { $_.Name -eq 'styles' } | Get-ChildItem -ID $_.ID -Recurse | Where-Object { (($isStylesOptimizingEnabled -eq "1" -and -not $_.Name.StartsWith("optimized")) -or ($isStylesOptimizingEnabled -ne "1")) } | Remove-Item -Confirm:$false -Force -Recurse
        $FontsFolders = Get-ChildItem -ID $themesFolder -Recurse | Where-Object { $_.Name -eq 'fonts' } | Get-ChildItem -ID $_.ID -Recurse | Remove-Item -Confirm:$false -Force -Recurse
        $imagesFolders = Get-ChildItem -ID $themesFolder -Recurse | Where-Object { $_.Name -eq 'images' } | Get-ChildItem -ID $_.ID -Recurse | Remove-Item -Confirm:$false -Force -Recurse

        Write-Host "End: Cleaning up theme folders..."

        Write-Host "Begin: Import Creative Exchange into Sitecore"
        $homeItem = Get-Item -Path "$($siteItem.ItemPath)/Home"
        $importArgs = Get-CreativeExchangeImportResponse -CurrentItem $homeItem -DeviceId $deviceId

        $responseObject = @{
            "Messages" = $importArgs.Messages
        }
        Set-HostProperty -HostWidth 9999
        [Newtonsoft.Json.JsonConvert]::SerializeObject($responseObject)
        Write-Host "End: Import Creative Exchange into Sitecore"

        Write-Host "Begin: Publish all front end assets"
        $targets = @()
        foreach($publishingTarget in [Sitecore.Publishing.PublishManager]::GetPublishingTargets($database)) {
            $targets += Get-Database -Name $publishingTarget[[Sitecore.FieldIDs]::PublishingTargetDatabase]
        }

        $languages = [Sitecore.Data.Managers.LanguageManager]::GetLanguages($database)

        # Item publish with children
        [Sitecore.Publishing.PublishManager]::PublishItem($themesFolderItem,$targets,$languages,$true,$true,$true)
        Write-Host "End: Publish all front end assets"
    }
}

I will quickly step through my process.

  • Both the Site item path and Device ID can be passed into the script.
  • There is a simple logic to determine whether asset optimisation is enabled for the site and globally on the Sitecore instance.
  • Delete all the Styles, Scripts, Fonts and Images under the specified Theme folder from the Site Theme Folder setting, but leaving the folder structure in tact (Styles, Scripts, Fonts, Images). If Asset Optimisation is used, the optimized files will not be deleted.
  • Runs the Creative Exchange import process
  • Publishes the entire theme folder to all publishing targets, for all languages that are available to the site. Utilising PublishingManager which will work for traditional publishing, as well as Publishing Service

  1. Here is a sample Front End Build script that can also be used on local developer instance and in the build pipeline.
<#
.SYNOPSIS

This script runs the Front End Build, with an option to trigger Creative Exchange Import to the Web Site

.DESCRIPTION

This script runs the Front End Build, with an option to trigger Creative Exchange Import process to the specified SXA web site. This can be used on both developer PC's, mainly a back end developer as a back end developer do not neccessarily require the most up to date front end build assets at all times.

.PARAMETER SitecoreWebSiteCreativeExchangeFolderPath
The main Creative Exchange web site folder path

.PARAMETER CreativeExchangeMediaLibraryPath
The Media Library path inside Creative Exchange web site folder, mainly where the final front end build assets will be located

.PARAMETER Username
The Sitecore username. Uses sitecore\admin by default.

.PARAMETER Password
The Sitecore user's password. Uses 'b' by default, because 'b' is cool.

.PARAMETER RunCreativeExchangeImport
Whether the Creative Exchange Import should be run. This should be false when run in the Build pipeline, as Creative Exchange Import should only be run in Release pipeline.
When running on a local developer PC, this can be set to true, as the compiled front end static assets will copy into the Creative Exchange folder of the web site.

.PARAMETER RunYarn
Whether the Yarn install and build should be run.  This is false when calling from the FED webpack build.

.PARAMETER SitecoreHost
The Content Management Sitecore Host. eg. https://sc911xc91xp0.dev.local

.PARAMETER ScriptSitecorePath
The SPE script located within /sitecore/system/Modules/PowerShell/Script Library/SXA/SXA - Creative Exchange/Web API/ for Creative Exchange Import

.INPUTS

None. You cannot pipe objects to this script.

.OUTPUTS

None.

.EXAMPLE

PS> .\FrontEndBuild.ps1 -SitecoreWebSiteCreativeExchangeFolderPath C:\inetpub\wwwroot\acme.sc\App_Data\packages\CreativeExchange\ -CreativeExchangeMediaLibraryPath FileStorage\AU\-\media\Themes\Acme\Explosives\AU\Master -Username sitecore\admin -Password b -RunCreativeExchangeImport $true -RunYarn $true -SitecoreHost https://acme.sc -ScriptSitecorePath /enhanced-ce-import

#>

param(
    [string] $SitecoreWebSiteCreativeExchangeFolderPath = "C:\inetpub\wwwroot\acme-sc911xc91xp0-cm.dev.local\App_Data\packages\CreativeExchange\",
    [string] $CreativeExchangeMediaLibraryPath = "FileStorage\acme\-\media\Themes\ACME\Explosives\Master",
    [string] $Username = "sitecore\admin",
    [string] $Password = "b",
    [bool]   $RunCreativeExchangeImport = $true,
    [bool]   $RunYarn = $true,
    [string] $SitecoreHost = "https://acme.sc",
    [string] $ScriptSitecorePath = "/enhanced-ce-import"
)

Push-Location $PSScriptRoot

if ($RunYarn) {
  try {
    yarn install
    if($LASTEXITCODE -eq 1)
    {
      Write-Error "Yarn Install Failed"
      exit 1
    }

    yarn build

    if($LASTEXITCODE -eq 1)
    {
      Write-Error "Yarn Build Failed"
      exit 1
    }
  }
  catch {
    exit 1
  }
}

if ($RunCreativeExchangeImport) {
    Write-Host "Build finished. Starting Creative Exchange Import."

    Pop-Location
    if ([System.IO.Directory]::Exists($SitecoreWebSiteCreativeExchangeFolderPath)) {
        Remove-Item -Recurse -Force $SitecoreWebSiteCreativeExchangeFolderPath
    }

    $FinalFrontEndPath = $SitecoreWebSiteCreativeExchangeFolderPath + $CreativeExchangeMediaLibraryPath
    $FinalFrontEndScriptsPath = $FinalFrontEndPath + "\Scripts"
    $FinalFrontEndSXAScriptsPath = $FinalFrontEndPath + "\SXA Scripts"
    $FinalFrontEndStylesPath = $FinalFrontEndPath + "\Styles"
    $FinalFrontEndFontsPath = $FinalFrontEndPath + "\fonts"

    New-Item -ItemType Directory -Path $FinalFrontEndPath -Force
    New-Item -ItemType Directory -Path $FinalFrontEndScriptsPath -Force
    New-Item -ItemType Directory -Path $FinalFrontEndStylesPath -Force
    New-Item -ItemType Directory -Path $FinalFrontEndSXAScriptsPath -Force
    New-Item -ItemType Directory -Path $FinalFrontEndFontsPath -Force

    # Theme JavaScript
    Copy-Item -Path "$PSScriptRoot\dist\js\*.js" -Destination $FinalFrontEndScriptsPath -Recurse -Force
    # SXA scripts that have been customised
    Copy-Item -Path "$PSScriptRoot\src\js\sxa\custom\*" -Destination $FinalFrontEndSXAScriptsPath -Recurse -Force
    # Theme styles
    Copy-Item -Path "$PSScriptRoot\dist\themes\Master\*.css" -Destination $FinalFrontEndStylesPath -Recurse -Force
    # Theme fonts
    Copy-Item -Path "$PSScriptRoot\src\fonts" -Destination $FinalFrontEndPath -Recurse -Force

    # Trigger the Creative Exchange Import Sitecore Powershell Extension Script
    & "$PSScriptRoot\RestfulV2-QueryStringAuth.ps1" -Username $Username -Password $Password -SitecoreHost $SitecoreHost -ScriptSitecorePath $ScriptSitecorePath -Parameters SitePath=/sitecore/content/Habitat SXA Sites/Habitat Home
}
  1. In the release pipeline, trigger the SPE to import the front end files as provided in Step 2.

For more information, please visit my blog post on https://medium.com/@Vincent_Lui/sitecore-experience-accelerator-creative-exchange-import-b166c24671da

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.