Ruling the continuous integration seas with Sitecore.Ship - Part 2: fileupload
Deploying Sitecore items with Sitecore.Ship
As I mentioned in the previous post
Sitecore.Ship can be used to install Sitecore update
or zip
packages by posting HTTP requests to the Sitecore site.
Configuration
I forgot to talk about the configuration in the previous post so let's have a look at that now. The Sitecore.Ship configuration is split into two parts:
- The
ship.config
file (located in App_Config\Include) contains the patchedIgnoreUrlPrefixes
attribute to include the/services/
url part which Sitecore.Ship is using. - The
web.config
is updated with apackageInstallation
element. The default values of this element are:<packageInstallation enabled="true" allowRemote="false" allowPackageStreaming="false" recordInstallationHistory="false" />
The attributes are pretty self explanatory. I'll get to therecordInstallationHistory
in a later post. Just make sure it isfalse
otherwise there will be errors about a missing PackageId.
Uploading and installing a package
One of the most useful commands of Sitecore.Ship is fileupload
. When you issue an HTTP request to <website>/services/package/install/fileupload
you can upload and install a Sitecore package.
The wiki describes that you need to provide the path of the package as form-data in the request. Lets have look how that is done exactly.
Postman
The easiest way to test the commands Sitecore.Ship offers is to use an HTTP/REST client such as Postman, which I'm using here.
- Once you've started Postman you'll see this screen:
- Change the following fields to do a post request to upload and install a package:
- Note that the value of the Key parameter (
path
in this example) is actually irrelevant, it can be any value. - Once the value type is set to
File
an Open file dialog can be used to select the file to upload. - Now press the blue Send button to do the post request.
If everything went well output shows the Sitecore IDs and path of the items that were in the package and have been installed:
So far so good, but we don't want to use Postman manually in order to upload and install packages for every deployment right? We need a solution that can be automated and used in a continuous integration setup.
cURL
I first looked into PowerShell and the Invoke-RestMethod
command but it appeared that OOTB this method does not support multipart form data, which is required to call the fileupload
command.
There is a workaround to create the required multipart boundaries in the request but I did not like this approach. I looked for another solution and found cURL.
cURL is a very powerful commandline application to script HTTP jobs. Getting the syntax right can be a little tricky although there is quite some documentation. Luckily Postman can generate various scripts including one for cURL:
However the cURL script in the screenshot above contains a lot of unncessesary statements and actually gives errors. I've found that this is the minimal cURL syntax which works for me:
curl -F "path=@<path to update or zip package>" 'http://<website>/services/package/install/fileupload'
Sofar we've only replaced Postman with cURL, but since cURL is a commandline tool it can be easily called from a script during a deployment process as we'll see next.
PowerShell
In the Gist below you can see the deploy-sitecorepackage.ps1 script which I use to upload and deploy Sitecore packages.
I actually prefer to use the more verbose cURL syntax (e.g. --form
instead of -F
) because I believe the intention
of the script is much more clear to the reader who might not know the syntax well.
A full description of the parameters can be found in the cURL manual.
deploy-sitecorepackage.ps1
<#
This function uploads & installs the specified Sitecore update package to the given $SiteUrl.
It uses cURL (http://curl.haxx.se/) to post a request to a Sitecore website which has Sitecore Ship installed.
Example usage:
.\deploy-sitecorepackage.ps1 mysite.dev "C:\Project\Build\Artifacts\1-mysite-templates.update" 60 300
#>
Param(
[Parameter(Position=0, Mandatory=$true)]
[string]$SiteUrl,
[Parameter(Position=1, Mandatory=$true)]
[string]$UpdatePackagePath,
[Parameter(Position=2)]
[ValidateRange(0, 99999)]
[int]$ConnectionTimeOutInSeconds = 300,
[Parameter(Position=3)]
[ValidateRange(0, 99999)]
[int]$MaxTimeOutInSeconds = 900
)
$fileUploadUrl = "$SiteUrl/services/package/install/fileupload"
$curlPath = .\get-curlpath.ps1
$curlCommand= "$curlPath --show-error --silent --connect-timeout $ConnectionTimeOutInSeconds --max-time $MaxTimeOutInSeconds --form ""filename=@$UpdatePackagePath"" $fileUploadUrl"
Write-Output "INFO: Starting Invoke-Expression: $curlCommand"
Invoke-Expression $curlCommand
The deploy-sitecorepackage.ps1 script uses another script called get-curlpath.ps1 to obtain the path to the cURL executable.
get-curlpath.ps1
<#
This script returns the full path of the curl.exe.
#>
$curlExe = 'curl.exe'
$curlPath = Resolve-Path "$PSScriptRoot\..\tools\curl-7.33.0-win64-nossl\$curlExe" # This is the path on the local dev machine.
if (-not (Test-Path $curlPath))
{
# Fall-back to use curl.exe located in the same location as the script.
if (Test-Path "$PSScriptRoot\$curlExe")
{
$curlPath = "$PSScriptRoot\$curlExe"
}
else
{
Write-Error "ERROR: $curlPath not found."
Exit
}
}
$curlPath
These PowerShell scripts can now easily be used in continuous integration & delivery tools such as Octopus Deploy or Microsoft Release Management.
What's next?
In the next post I'll explain how Sitecore.Ship can be used to record the package installation history.