Skip to content

Deployment

Partner Documentation

Deploy your DCS-integrated site to Azure Static Web Apps.

Deployment Overview

DCS uses a staged deployment model:

Code Push → Build → Deploy to Dev → Review → Deploy to Production

Environments

EnvironmentPurposeTrigger
DevelopmentPreview changesPush to release branch
DraftPR previewsPull request
ProductionLive siteManual approval

Automated Deployment

Push to Development

When you push to a release branch:

  1. GitHub Actions triggers
  2. Site builds
  3. Deploys to development environment
  4. Portal shows "Preview Available"

Draft Deployments

For development requests:

  1. Copilot creates PR
  2. PR preview deploys automatically
  3. Stakeholders can review
  4. Approval triggers dev deployment

Production Deployment

  1. Review changes in development
  2. Click "Approve for Production" in portal
  3. Release branch merges to main
  4. Production deploys automatically
  5. Version tag created

GitHub Actions Workflows

Basic Deployment

yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches:
      - main
      - 'release/**'

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          
      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 9
          
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
        
      - name: Build
        run: pnpm build
        env:
          VITE_DCS_API: ${{ secrets.VITE_DCS_API }}
          VITE_SITE_ID: ${{ secrets.VITE_SITE_ID }}
          
      - name: Deploy
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_SWA_TOKEN }}
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          action: 'upload'
          app_location: '/'
          output_location: '.vitepress/dist'
          skip_app_build: true

Environment-Specific Deployment

yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches:
      - main
      - 'release/**'

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      environment: ${{ steps.determine-env.outputs.environment }}
      
    steps:
      - uses: actions/checkout@v4
      
      - name: Determine environment
        id: determine-env
        run: |
          if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
            echo "environment=production" >> $GITHUB_OUTPUT
          else
            echo "environment=development" >> $GITHUB_OUTPUT
          fi
          
      - name: Setup and build
        # ... build steps ...
        
      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: site
          path: .vitepress/dist
          
  deploy-dev:
    needs: build
    if: needs.build.outputs.environment == 'development'
    runs-on: ubuntu-latest
    environment: development
    
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: site
          path: dist
          
      - name: Deploy to development
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_SWA_DEV_TOKEN }}
          action: 'upload'
          app_location: 'dist'
          skip_app_build: true
          
  deploy-prod:
    needs: build
    if: needs.build.outputs.environment == 'production'
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: site
          path: dist
          
      - name: Deploy to production
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_SWA_PROD_TOKEN }}
          action: 'upload'
          app_location: 'dist'
          skip_app_build: true

PR Preview Deployment

yaml
# .github/workflows/preview.yml
name: PR Preview

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  deploy-preview:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Build
        # ... build steps ...
        
      - name: Deploy preview
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_SWA_TOKEN }}
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          action: 'upload'
          app_location: '/'
          output_location: '.vitepress/dist'
          
      - name: Comment preview URL
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '🚀 Preview deployed! URL: https://preview-${{ github.event.pull_request.number }}.azurestaticapps.net'
            })

Build Configuration

VitePress Build

json
{
  "scripts": {
    "build": "vitepress build docs",
    "build:prod": "NODE_ENV=production vitepress build docs"
  }
}

Environment Variables

yaml
- name: Build
  run: pnpm build
  env:
    # Required
    VITE_DCS_API: ${{ secrets.VITE_DCS_API }}
    VITE_SITE_ID: ${{ secrets.VITE_SITE_ID }}
    
    # Optional
    VITE_ANALYTICS_ID: ${{ secrets.ANALYTICS_ID }}
    NODE_ENV: production

Azure Static Web Apps

Resource Configuration

bash
# Create development SWA
az staticwebapp create \
  --name my-site-dev \
  --resource-group my-resource-group \
  --location eastus2 \
  --sku Free \
  --branch release/1.0.0 \
  --source https://github.com/org/my-site

# Create production SWA
az staticwebapp create \
  --name my-site-prod \
  --resource-group my-resource-group \
  --location eastus2 \
  --sku Standard \
  --branch main \
  --source https://github.com/org/my-site

Custom Domains

bash
# Add custom domain
az staticwebapp hostname set \
  --name my-site-prod \
  --hostname www.mysite.com

# Verify domain
az staticwebapp hostname show \
  --name my-site-prod \
  --hostname www.mysite.com

staticwebapp.config.json

json
{
  "navigationFallback": {
    "rewrite": "/index.html",
    "exclude": [
      "/images/*",
      "/fonts/*",
      "/*.ico",
      "/*.xml"
    ]
  },
  "routes": [
    {
      "route": "/api/*",
      "rewrite": "/api/{*}"
    }
  ],
  "responseOverrides": {
    "404": {
      "rewrite": "/404.html"
    }
  },
  "globalHeaders": {
    "X-Frame-Options": "DENY",
    "X-Content-Type-Options": "nosniff",
    "Referrer-Policy": "strict-origin-when-cross-origin",
    "Content-Security-Policy": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
  }
}

Rollback

Via Portal

  1. Go to Site → Revisions
  2. Find the version to rollback to
  3. Click "Rollback"
  4. Confirm

Via GitHub

bash
# Revert the problematic commit
git revert HEAD
git push origin main

Via Azure CLI

bash
# List builds
az staticwebapp build list \
  --name my-site-prod

# Rollback to specific build
az staticwebapp environment delete \
  --name my-site-prod \
  --environment-name preview

Monitoring Deployments

GitHub Actions

Monitor workflows at:

https://github.com/org/repo/actions

Azure Portal

Monitor deployments in Azure Portal:

  • Static Web Apps → Your App → Deployment History

DCS Portal

The portal shows:

  • Current version
  • Deployment status
  • Last deployment time
  • Environment URLs

Troubleshooting

Build Failures

  1. Check workflow logs
  2. Verify environment variables
  3. Test build locally: pnpm build
  4. Check for missing dependencies

Deployment Failures

  1. Verify SWA token is valid
  2. Check output directory path
  3. Verify file size limits (< 250MB total)
  4. Check for unsupported file types

Preview Not Working

  1. Ensure PR previews are enabled
  2. Check for workflow permissions
  3. Verify branch protection rules

Custom Domain Issues

  1. Verify DNS configuration
  2. Wait for DNS propagation (up to 48 hours)
  3. Check SSL certificate status
  4. Verify domain ownership

Best Practices

1. Use Environments

Separate secrets and approvals by environment:

yaml
jobs:
  deploy-prod:
    environment: production
    # Requires manual approval

2. Artifact Caching

yaml
- name: Cache dependencies
  uses: actions/cache@v4
  with:
    path: node_modules
    key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}

3. Health Checks

Add a health check after deployment:

yaml
- name: Health check
  run: |
    response=$(curl -s -o /dev/null -w "%{http_code}" https://mysite.com)
    if [ "$response" != "200" ]; then
      echo "Health check failed"
      exit 1
    fi

4. Deployment Notifications

yaml
- name: Notify on success
  if: success()
  uses: slackapi/slack-github-action@v1
  with:
    payload: |
      {"text": "✅ Deployment successful: ${{ github.ref }}"}

Next Steps

Duff Cloud Services Documentation