Deployment
Vertical applications built on the EnterpriseAI platform are deployed to Azure App Service using GitHub Actions CI/CD pipelines. The EAI CLI provides commands to set up deployment configuration, trigger builds, and monitor deployment status.
Deployment Architecture
The deployment pipeline:
- Developer pushes code to the
mainbranch (or creates a PR) - GitHub Actions runs the CI/CD workflow (lint, test, build)
- On success, the built application is deployed to Azure App Service
- Azure App Service serves the Next.js application
- The BFF proxy in the deployed app connects to the platform's PublicAPI gateway
Setting Up Deployment
Use the EAI CLI to configure deployment for your vertical:
# Set up deployment configuration (creates GitHub Actions workflow and Azure config)
eai deploy setup
This command:
- Creates a GitHub Actions workflow file at
.github/workflows/deploy.yml - Configures the Azure App Service deployment target
- Sets the
APP_BASE_PATHfor multi-app deployments - Prompts for required Azure credentials and secrets
Required GitHub Secrets
After running eai deploy setup, configure these secrets in your GitHub repository settings:
| Secret | Description |
|---|---|
AZURE_WEBAPP_PUBLISH_PROFILE | Azure App Service publish profile XML |
BASE_URL_PUBLIC_API | PublicAPI gateway URL |
ENTRA_TENANT_ID | Entra CIAM tenant GUID |
ENTRA_CLIENT_ID | App registration client ID |
ENTRA_CLIENT_SECRET | App registration client secret |
AUTH_SECRET | Auth.js session encryption key |
GitHub Actions CI/CD Pipeline
The deployment workflow runs on every push to main and on pull requests. Here is a typical workflow structure:
# .github/workflows/deploy.yml
name: Deploy to Azure
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run test
- run: npm run build
env:
BASE_URL_PUBLIC_API: ${{ secrets.BASE_URL_PUBLIC_API }}
ENTRA_TENANT_NAME: ${{ secrets.ENTRA_TENANT_NAME }}
ENTRA_TENANT_ID: ${{ secrets.ENTRA_TENANT_ID }}
ENTRA_CLIENT_ID: ${{ secrets.ENTRA_CLIENT_ID }}
ENTRA_CLIENT_SECRET: ${{ secrets.ENTRA_CLIENT_SECRET }}
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
APP_BASE_PATH: /${{ vars.APP_NAME }}
- uses: actions/upload-artifact@v4
with:
name: build
path: .next/
deploy:
needs: build-and-test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: .next/
- uses: azure/webapps-deploy@v3
with:
app-name: ${{ vars.AZURE_WEBAPP_NAME }}
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
package: .
Pipeline Stages
| Stage | Purpose | Runs On |
|---|---|---|
| Lint | ESLint checks for code quality | All pushes and PRs |
| Test | Jest unit tests and Playwright E2E tests | All pushes and PRs |
| Build | Next.js production build with environment variables | All pushes and PRs |
| Deploy | Deploy to Azure App Service | Only on main branch pushes |
Triggering and Monitoring Deployments
# Trigger a deployment manually
eai deploy trigger
# Check deployment status
eai deploy status
# View deployment logs
eai deploy logs
Environment Configuration for Production
Production environment variables are configured in the Azure App Service application settings, not in .env files. Use the Azure Portal or Azure CLI to set them:
# Set environment variables on Azure App Service
az webapp config appsettings set \
--resource-group myResourceGroup \
--name my-vertical-app \
--settings \
BASE_URL_PUBLIC_API="https://publicapi.example.com" \
ENTRA_TENANT_NAME="myorgciam" \
ENTRA_TENANT_ID="<guid>" \
ENTRA_CLIENT_ID="<guid>" \
ENTRA_CLIENT_SECRET="<secret>" \
AUTH_SECRET="<base64-key>" \
TENANT_KEYS="permits,grants" \
APP_BASE_PATH="/my-vertical"
Required Environment Variables
| Variable | Description | Example |
|---|---|---|
BASE_URL_PUBLIC_API | PublicAPI gateway URL | https://publicapi.example.com |
TENANT_KEYS | Comma-separated list of tenant slugs | permits,grants |
TENANT_{SLUG}_ID | Configurator tenant ID for each tenant | <guid> |
WORKFLOW_{SLUG}_ID | Configurator workflow ID for each tenant | <guid> |
ENTRA_TENANT_NAME | Entra CIAM tenant name | myorgciam |
ENTRA_TENANT_ID | Entra CIAM tenant GUID | <guid> |
ENTRA_CLIENT_ID | App registration client ID | <guid> |
ENTRA_CLIENT_SECRET | App registration client secret | <secret> |
ENTRA_SCOPES | OAuth scopes | email offline_access openid profile |
AUTH_SECRET | Auth.js session encryption key | <base64-key> |
NEXT_PUBLIC_APP_NAME | Application display name | Permits Portal |
APP_BASE_PATH | Base path for multi-app deployments | /permits |
BasePath Configuration
When multiple vertical applications are deployed to the same domain, each app is served under a unique base path (e.g., example.com/permits, example.com/grants). The APP_BASE_PATH environment variable configures this.
How BasePath Works
- Set
APP_BASE_PATH=/permitsin your environment - Next.js is configured to use this as the
basePathinnext.config.ts:
// next.config.ts
const nextConfig: NextConfig = {
basePath: process.env.APP_BASE_PATH || '',
// ... other config
};
- All routes are automatically prefixed:
/dashboardbecomes/permits/dashboard - Static assets, API routes, and auth callbacks all respect the base path
- The BFF proxy adjusts its paths accordingly
Important Considerations
- The
APP_BASE_PATHmust start with/but not end with/ - Auth.js callback URLs must include the base path (handled automatically)
- Links within the application should use Next.js
<Link>component, which respects the base path - External links or hardcoded URLs need manual base path prefixing
import Link from 'next/link';
// Correct -- Link component handles basePath automatically
<Link href="/dashboard">Dashboard</Link>
// Renders as: <a href="/permits/dashboard">Dashboard</a>
// For programmatic navigation
import { useRouter } from 'next/navigation';
const router = useRouter();
router.push('/dashboard'); // Navigates to /permits/dashboard
Verifying Deployment
After deployment, verify that your application is working correctly:
# Check platform connectivity
eai verify --tenant permits
# Or use the built-in verification endpoint
curl https://myapp.example.com/permits/api/eai/config
The verification checks:
- Next.js application is responding
- BFF proxy can reach PublicAPI
- Authentication flow is working
- Object types are seeded and available
- ResourceAPI CRUD operations succeed