Skip to content

Onboarding a New Site

Partner Documentation

This guide walks you through the complete process of adding a new customer site to DCS using the official npm packages.

Prerequisites

Before starting, ensure you have:

  • [ ] A VitePress or compatible website
  • [ ] Access to the site's GitHub repository
  • [ ] Node.js >= 18.0.0 installed
  • [ ] DCS partner account with admin access
  • [ ] Azure subscription with appropriate permissions (for deployment)

The fastest way to onboard a site is using the official CLI:

bash
# 1. Install the CLI globally
npm install -g @duffcloudservices/cli

# 2. Authenticate with DCS
dcs login

# 3. Navigate to your site's repository
cd /path/to/customer-site

# 4. Initialize DCS integration
dcs init --site-slug customer-site --site-name "Customer Site Name"

# 5. Install the CMS package
pnpm add @duffcloudservices/cms

# 6. Follow the generated integration plans
code .plans/

The CLI creates all necessary configuration files and generates step-by-step integration plans.


Step 1: Initialize DCS Configuration

bash
dcs init --site-slug customer-site --site-name "Customer Site Name"

This creates:

  • .dcs/site.yaml — Site identity
  • .dcs/pages.yaml — Page registry
  • .dcs/content.yaml — Text content (portal-managed)
  • .dcs/seo.yaml — SEO configuration (portal-managed)
  • .github/copilot-instructions.md — AI assistant context
  • .plans/ — Integration guides

Manual Setup (Alternative)

If you prefer manual setup, create the .dcs/ files by hand. Site identity and deployment settings live in .dcs/site.yaml:

yaml
site_name: Customer Site Name
site_slug: customer-site
portal_api_url: https://portal.duffcloudservices.com
azure:
  client_id: <app-registration-client-id>
  tenant_id: <azure-tenant-id>
  subscription_id: <azure-subscription-id>
swa_resource_id: /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Web/staticSites/customer-site

See the site.yaml reference for the complete schema. Pages go in .dcs/pages.yaml; content.yaml and seo.yaml are portal-managed.

Step 2: Install & Configure the CMS Package

Install Dependencies

bash
pnpm add @duffcloudservices/cms

Configure Vite Plugins

For VitePress sites:

typescript
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
import { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms/plugins'

export default defineConfig({
  vite: {
    plugins: [
      dcsContentPlugin(),
      dcsSeoPlugin()
    ]
  }
})

For standard Vite sites:

typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms/plugins'

export default defineConfig({
  plugins: [
    vue(),
    dcsContentPlugin(),
    dcsSeoPlugin()
  ]
})

Add Environment Variables

bash
# .env
VITE_SITE_SLUG=customer-site
VITE_API_BASE_URL=https://portal.duffcloudservices.com

Step 3: Implement Text Content Integration

Use the Composable

vue
<script setup lang="ts">
import { useTextContent } from '@duffcloudservices/cms'

const { t } = useTextContent({
  pageSlug: 'home',
  defaults: {
    'hero.title': 'Welcome to Our Site',
    'hero.subtitle': 'Building the future together',
    'cta.primary': 'Get Started'
  }
})
</script>

<template>
  <section class="hero">
    <h1>{{ t('hero.title') }}</h1>
    <p>{{ t('hero.subtitle') }}</p>
    <button>{{ t('cta.primary') }}</button>
  </section>
</template>

Add SEO Integration

vue
<script setup lang="ts">
import { useTextContent, useSEO } from '@duffcloudservices/cms'

const { t } = useTextContent({
  pageSlug: 'home',
  defaults: { /* ... */ }
})

// Apply SEO meta tags
const { applyHead } = useSEO('home')
applyHead()
</script>

Step 4: Register Pages

Create .dcs/pages.yaml:

yaml
pages:
  - slug: home
    path: /
    type: static
    title: Home
    deletable: false
    textKeys: []

  - slug: about
    path: /about
    type: static
    title: About Us
    deletable: true
    textKeys: []

  - slug: services
    path: /services
    type: static
    title: Services
    deletable: true
    textKeys: []

  - slug: contact
    path: /contact
    type: static
    title: Contact
    deletable: true
    textKeys: []

  - slug: blogs
    path: /blog
    type: index
    title: Blog
    deletable: false
    textKeys: []

Page Types

TypeUse CaseNotes
staticRegular pagesUser-editable content
indexCollection pagesBlog index, etc.
dynamicGenerated pagesAuto-created content

Step 5: Set Up GitHub Integration

Configure Azure authentication (OIDC)

Deployment uses OIDC federated credentials — no long-lived Azure secret and no static Static Web App token are stored in the repo. dcs init writes the three non-secret Azure identifiers into .dcs/site.yaml; you can alternatively set them as repository Variables (Settings → Secrets and variables → Actions → Variables):

AZURE_CLIENT_ID           # app registration (client) ID with a federated credential
AZURE_TENANT_ID           # Azure tenant ID
AZURE_SUBSCRIPTION_ID     # Azure subscription ID

The federated credential on the app registration (mapping master and release/** to the repo) is provisioned during onboarding. See GitHub Setup for details.

AI assistant guidance and deployment workflow

dcs init already generated both of these in Step 1:

  • .github/copilot-instructions.md — AI assistant context for the repo. Tailor it to the site (framework, styling conventions, text-content pattern).
  • .github/workflows/site-deploy.yml — the OIDC deployment workflow. It triggers on master (→ Production) and release/** (→ Preview), logs in with azure/login@v2, and fetches a short-lived Static Web App token from the DCS API at run time (POST /api/v1/sites/deployment-tokens). Do not hand-write it or add a static deployment token. See GitHub Setup for the workflow anatomy.

Step 6: Create Azure Resources

Static Web App (Development)

bash
az staticwebapp create \
  --name customer-site-dev \
  --resource-group dcs-sites-rg \
  --location eastus2 \
  --sku Free

Static Web App (Production)

bash
az staticwebapp create \
  --name customer-site-prod \
  --resource-group dcs-sites-rg \
  --location eastus2 \
  --sku Standard

Configure Custom Domain (Production)

bash
az staticwebapp hostname set \
  --name customer-site-prod \
  --hostname www.customer-site.com

Step 7: Register in DCS Portal

Create Site Record

  1. Log into the DCS admin portal
  2. Navigate to Sites → Add Site
  3. Fill in site details:
    • Slug: customer-site
    • Name: Customer Site Name
    • GitHub Repo: owner/customer-site
    • Dev SWA Resource ID: (from Azure)
    • Prod SWA Resource ID: (from Azure)

Assign to Company

  1. Navigate to Companies → Select Company
  2. Add site to company
  3. Set appropriate user permissions

Step 8: Verify Integration

Validate Configuration

bash
dcs validate

Test Text Overrides

  1. Create a test override in the portal
  2. Verify it appears on the dev site
  3. Delete the test override

Test Development Requests

  1. Submit a test request
  2. Verify GitHub issue is created
  3. Verify Copilot response
  4. Review and close test request

Onboarding Checklist

  • [ ] CLI installed and authenticated (dcs login)
  • [ ] Site initialized with dcs init
  • [ ] @duffcloudservices/cms package installed
  • [ ] Vite plugins configured
  • [ ] Text content composable implemented in pages
  • [ ] Pages registered in .dcs/pages.yaml
  • [ ] GitHub secrets configured
  • [ ] Deployment workflows added
  • [ ] Azure SWA resources created
  • [ ] Site registered in DCS portal
  • [ ] Configuration validated (dcs validate)
  • [ ] Integration tested end-to-end
  • [ ] Site owner trained on portal usage

Troubleshooting

Text overrides not appearing

  1. Check API endpoint in configuration
  2. Verify CORS settings on DCS server
  3. Check browser console for errors

Deployment failing

  1. Verify GitHub secrets are correct
  2. Check Azure SWA resource IDs
  3. Review workflow logs

GitHub integration issues

  1. Verify webhook configuration
  2. Check service principal permissions
  3. Review DCS server logs

Next Steps