Step 2 of 4 (50% complete)

Deploy Your Application & Set Up CI/CD

๐Ÿš€ Deploy Your Application & Set Up CI/CD

This guide shows you how to deploy your application using Optimizely Frontend Hosting, both manually and via CI/CD automation.

๐Ÿงช Manual Deployment (Recommended for First-Time Setup)

Follow these steps to deploy manually using PowerShell.

1. Install the Required PowerShell Module

Install-Module -Name EpiCloud -Scope CurrentUser -Force
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Import-Module EpiCloud

2. Set Your Credentials and Target Environment

$projectId = "<your_project_id>"
$clientKey = "<your_client_key>"
$clientSecret = "<your_client_secret>"
$targetEnvironment = "Test1"  # Or "Production" or "Test2"

3. Zip Your Next.js Application

Use this naming format:

<name>.head.app.<version>.zip

Example:

optimizely-one.head.app.20250714.zip

โš ๏ธ If the filename does not contain ".head.app.", the system will treat it as a .NET NuGet package!

4. Connect and Deploy

Connect-EpiCloud -ProjectId $projectId -ClientKey $clientKey -ClientSecret $clientSecret
$sasUrl = Get-EpiDeploymentPackageLocation
Add-EpiDeploymentPackage -SasUrl $sasUrl -Path .\optimizely-one.head.app.20250714.zip

Start-EpiDeployment `
  -DeploymentPackage "optimizely-one.head.app.20250714.zip" `
  -TargetEnvironment $targetEnvironment `
  -DirectDeploy `
  -Wait `
  -Verbose

๐Ÿค– Automating Deployment (PowerShell Script)

Once youโ€™ve done a few manual deployments, automate the process via script:

1. .zipignore Support

Add a .zipignore file to your project root to exclude unnecessary files.

Example .zipignore:

.next
node_modules
.env
.git
.vscode
.DS_Store

Customize the Script

Update:

  • $sourcePath โ†’ your Next.js project path
  • $targetEnvironment โ†’ "Test1", "Test2", or "Production"

3. Set Required Environment Variables

Youโ€™ll need:

  • OPTI_PROJECT_ID
  • OPTI_CLIENT_KEY
  • OPTI_CLIENT_SECRET

Set them in PowerShell like so:

$env:OPTI_PROJECT_ID = "<your_project_id>"
$env:OPTI_CLIENT_KEY = "<your_client_key>"
$env:OPTI_CLIENT_SECRET = "<your_client_secret>"

Or add them to your system environment variables.

4. Run the Script

Save the script as deploy-optimizely.ps1 and run:

.\deploy-optimizely.ps1

5. Full Deployment Script (PowerShell)

if (-not $env:OPTI_PROJECT_ID -or -not $env:OPTI_CLIENT_KEY -or -not $env:OPTI_CLIENT_SECRET) {
    Write-Host "Missing environment variables." -ForegroundColor Red
    exit 1
}

Install-Module -Name EpiCloud -Scope CurrentUser -Force -ErrorAction SilentlyContinue
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
Import-Module EpiCloud

$projectId = $env:OPTI_PROJECT_ID
$clientKey = $env:OPTI_CLIENT_KEY
$clientSecret = $env:OPTI_CLIENT_SECRET
$targetEnvironment = "Test1"
$sourcePath = "C:\Workspace\Personal\optimizely-one"

$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$zipName = "optimizely-one.head.app.$timestamp.zip"
$zipPath = ".\$zipName"

if (Test-Path $zipPath) { Remove-Item $zipPath }

$zipIgnorePath = Join-Path $sourcePath ".zipignore"
$excludeRoot = @()
if (Test-Path $zipIgnorePath) {
    $excludeRoot = Get-Content $zipIgnorePath | Where-Object { $_ -and $_ -notmatch "^#" }
}
$rootExcludes = $excludeRoot | ForEach-Object { Join-Path $sourcePath $_ }

$includeFiles = Get-ChildItem -Path $sourcePath -Recurse -File | Where-Object {
    foreach ($ex in $rootExcludes) {
        if ($_.FullName -like "$ex\*" -or $_.FullName -eq $ex) {
            return $false
        }
    }
    return $true
}

if ($includeFiles.Count -eq 0) {
    Write-Host "ERROR: No files to archive." -ForegroundColor Red
    exit 1
}

$tempPath = Join-Path $env:TEMP "nextjs-build-zip"
if (Test-Path $tempPath) { Remove-Item -Recurse -Force $tempPath }
New-Item -ItemType Directory -Path $tempPath | Out-Null

foreach ($file in $includeFiles) {
    $relativePath = $file.FullName.Substring($sourcePath.Length).TrimStart('\')
    $destPath = Join-Path $tempPath $relativePath
    $destDir = Split-Path -Path $destPath -Parent
    if (-not (Test-Path -LiteralPath $destDir)) {
        New-Item -ItemType Directory -Path $destDir -Force | Out-Null
    }
    Copy-Item -LiteralPath $file.FullName -Destination $destPath -Force
}

Compress-Archive -Path "$tempPath\*" -DestinationPath $zipPath
Remove-Item -Recurse -Force $tempPath

Connect-EpiCloud -ProjectId $projectId -ClientKey $clientKey -ClientSecret $clientSecret
$sasUrl = Get-EpiDeploymentPackageLocation
Add-EpiDeploymentPackage -SasUrl $sasUrl -Path $zipPath

Start-EpiDeployment `
    -DeploymentPackage $zipName `
    -TargetEnvironment $targetEnvironment `
    -DirectDeploy `
    -Wait `
    -Verbose

๐Ÿ”„ CI/CD with GitHub Actions

Step 1: Add Secrets

Go to GitHub โ†’ Repo โ†’ Settings โ†’ Secrets โ†’ Actions โ†’ New Repository Secret

NameValue
OPTI_PROJECT_IDYour Optimizely Project ID
OPTI_CLIENT_KEYYour Optimizely Client Key
OPTI_CLIENT_SECRETYour Optimizely Client Secret

Step 2: PowerShell Script for GitHub

Save this as scripts/deploy.ps1 in your repo:

if (-not $env:OPTI_PROJECT_ID -or -not $env:OPTI_CLIENT_KEY -or -not $env:OPTI_CLIENT_SECRET) {
    Write-Host "Missing required environment variables." -ForegroundColor Red
    exit 1
}

Install-Module -Name EpiCloud -Scope CurrentUser -Force -ErrorAction SilentlyContinue
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
Import-Module EpiCloud

$projectId = $env:OPTI_PROJECT_ID
$clientKey = $env:OPTI_CLIENT_KEY
$clientSecret = $env:OPTI_CLIENT_SECRET
$targetEnvironment = "Test1"

$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$zipName = "optimizely-one-github.head.app.$timestamp.zip"
$zipPath = ".\$zipName"

Compress-Archive -Path * -DestinationPath $zipPath

if (-not (Test-Path $zipPath)) {
    Write-Host "Failed to create ZIP file." -ForegroundColor Red
    exit 1
}

Connect-EpiCloud -ProjectId $projectId -ClientKey $clientKey -ClientSecret $clientSecret
$sasUrl = Get-EpiDeploymentPackageLocation
Add-EpiDeploymentPackage -SasUrl $sasUrl -Path $zipPath

Start-EpiDeployment `
    -DeploymentPackage $zipName `
    -TargetEnvironment $targetEnvironment `
    -Wait

Step 3: GitHub Actions Workflow

Create a file .github/workflows/deploy.yml:

name: Deploy to Optimizely

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: windows-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Run deployment script
        shell: pwsh
        run: ./scripts/deploy.ps1
        env:
          OPTI_PROJECT_ID: ${{ secrets.OPTI_PROJECT_ID }}
          OPTI_CLIENT_KEY: ${{ secrets.OPTI_CLIENT_KEY }}
          OPTI_CLIENT_SECRET: ${{ secrets.OPTI_CLIENT_SECRET }}

Summary

Deployment TypeTools UsedRecommended For
ManualPowerShellFirst-time deployments, debugging
AutomatedGitHub CI/CD + PowerShellProduction deployments, automation

Once you're confident with manual deployments, switch to CI/CD for consistency and speed ๐Ÿš€

Have questions? I'm here to help!

Contact Me