XM Cloud - Redeploy to Vercel from XM Cloud

Jared Arnofsky,XM CloudVercelSitecore PowerShell

Summary

In XM Cloud we built a site factory so that content authors spin up sites with default content fast.

Sitecore's multi site plugin is dependent on your site being in the config.js Sites array, which happens at build time. Additionally, our implementation utilizes static site generation. So, we needed to identify a way for a content author to run a new build to Vercel from Sitecore when they create and publish a new site.

The Solution

After going through the Vercel Rest Api (opens in a new tab) we identified the best path forward was to find out our last successful deployment and redeploy it. This made it so we didn't need any access to our repo, knew the last stable release was going to redeploy, and a new Vercel build would be triggered causing our Sites array to populate and our static site generation to occur.

From the Sitecore side the solution was to add a context menu script to Sitecore so when you right click on a specific site node you have a Redeploy to Vercel script option. We leveraged built in rules to make it only show on our parent site nodes.

The Sitecore PowerShell Extensions documentation (opens in a new tab) has some more details on adding a context menu script with some example blog posts. Additionally you can look at some of the scripts that come with XM Cloud in SPE as examples.

Additionally, we added an api call to add a new domain to Vercel during our redeploy. With this addition the workflow for content authors would be the following:

  1. Make a new site
  2. Add a site url for the new site's Site Grouping Site item in the host name field. (assuming using default SXA)
  3. Publish site to experience edge
  4. Select site node and Redeploy to Vercel

To leverage Vercel's API you need to set up an access token (opens in a new tab).

You also need to identify your project id and team id in the Vercel portal, or in the .vercel folder in your headless app after your initial deploy.

We leveraged the following api calls:

  1. /v6/deployments
  2. /v13/deployments
  3. /v10/projects

The Vercel Rest Api (opens in a new tab) is a great resource to further understand these calls, additional parameters and much more.

Redeploy To Vercel Script

This PowerShell script will make a DNS entry in Vercel and Redeploy the code causing your newly created site to appear in the config.js. Please note that the automatic DNS entry is set up to only take the first value in the Site Grouping Site item.

function GetLatestDeploymentId {
    $uri = $vercelBaseApiUrl + "/v6/deployments?limit=1&state=READY&projectId=" + $projectId + "&teamId=" + $teamId + "&target=" + $target + "&name=" + $projectName

    $headers = @{Authorization = "Bearer $token"}

    $response = Invoke-WebRequest -Method Get -Uri $uri -Headers $headers -UseBasicParsing

    if($response.StatusCode -eq 200)
    {
        $result = ConvertFrom-Json $response.content

        $latestDeploymentId = $result.deployments[0].uid

        Write-Host "Latest Deployment Id:" $latestDeploymentId

        return $latestDeploymentId
    }
}

function GetDeploymentStatus {
    param 
    (
        $DeploymentId
    )

    $uri = $vercelBaseApiUrl + "/v13/deployments/" + $DeploymentId + "?projectId=" + $projectId + "&teamId=" + $teamId

    $headers = @{Authorization = "Bearer $token"}

    $response = Invoke-WebRequest -Method Get -Uri $uri -Headers $headers -UseBasicParsing

    if($response.StatusCode -eq 200)
    {
        $result = ConvertFrom-Json $response.content

        $status = $result.status

        return $status
    }
}

function RedeployById {
    param 
    (
        $DeploymentId
    )

    $uri = $vercelBaseApiUrl + "/v13/deployments?projectId=" + $projectId + "&teamId=" + $teamId

    $headers = @{Authorization = "Bearer $token"}

    $body = @{deploymentId = "$DeploymentId"; name = "$projectName"; target = "$target"}

    $response = Invoke-WebRequest -Method Post -Uri $uri -Headers $headers -Body ($body | ConvertTo-Json) -ContentType "application/json" -UseBasicParsing

    if($response.StatusCode -eq 200)
    {
        $result = ConvertFrom-Json $response.content

        $redeploymentId = $result.id
        $redeploymentStatus = $result.status

        Write-Host "Redeployment Started"
        Write-Host "Redeployment Id" $redeploymentId
        Write-Host "Redeployment Status" $redeploymentStatus

        Do
        {
            Start-Sleep -Seconds 30 #call api every 30 seconds
            $status = GetDeploymentStatus -DeploymentId $redeploymentId
            Write-Host "Redeployment Status" $status
        }
        While ($status -ne "READY")

        Write-Host "Redeployment Complete"

    }
}

function AddNewDomain {
    param 
    (
        $DomainName
    )

    $uri = $vercelBaseApiUrl + "/v10/projects/"+ $projectId + "/domains?teamId=" + $teamId

    $headers = @{Authorization = "Bearer $token"}

    $body = @{name = "$DomainName"}

    $response = Invoke-WebRequest -Method Post -Uri $uri -Headers $headers -Body ($body | ConvertTo-Json) -ContentType "application/json" -UseBasicParsing

    if($response.StatusCode -eq 200)
    {
        $result = ConvertFrom-Json $response.content

        $domainNameAdded = $result.name

        Write-Host "Domain Added" $domainNameAdded
    }
}

Try
{
    $vercelBaseApiUrl = "https://api.vercel.com"
    $target = "production"
    
    $token = "[Vercel Token]"
    $projectName = "[Vercel Project Name]"
    $projectId = "[Vercel Project Id]"
    $teamId = "[Vercel Organization Id]"

    $currentSiteItem = Get-Item .
	$currentSiteName = $currentSiteItem.Name
	$currentSitePath = $currentSiteItem.Paths.Path
	$currentSiteDefinitionItem = Get-Item "master:$currentSitePath\Settings\Site Grouping\$currentSiteName"
	$currentSiteHostNameField = $currentSiteDefinitionItem.Fields["HostName"].Value
	$domainName = $currentSiteHostNameField.Split("|")[0]
	
    $latestDeploymentId = GetLatestDeploymentId
    
    AddNewDomain -DomainName $domainName
    
    RedeployById -DeploymentId $latestDeploymentId
}
Catch
{
    Write-Host "An error occurred: " + $_
}

Conclusion

In our use case our content authors needed the ability to create sites on demand fairly often without developer help. Not all multi site instances in XM cloud need this, but this approach certainly makes it possible.

Seeing what is possible with Vercel's Rest API and how we can integrate with Sitecore is really exciting. I am eager to find out more use cases in the future.