From 9a11025a318916511bb544854afe5b1656a69a4b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 22 Dec 2025 12:33:48 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=9A=20ci:=20implement=20Phase=200.5=20?= =?UTF-8?q?CI/CD=20Pipeline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement complete CI/CD pipeline for Luminous project: - GitHub Actions for .NET build and test (0.5.1) - Build, test, coverage collection - Security scanning for vulnerable packages - GitHub Actions for Angular build and test (0.5.2) - Lint, typecheck, unit tests - Production and staging builds - Security audit and bundle analysis - GitHub Actions for Bicep deployment (0.5.3) - Bicep validation and linting - What-if previews for all environments - Deployment to dev/staging/prod - Environment-specific deployments (0.5.4) - Azure OIDC authentication - Separate workflows for infrastructure and application - Approval gates for production - Dependabot for dependency updates (0.5.5) - NuGet, npm, GitHub Actions, Docker ecosystems - Grouped updates (Microsoft, Angular, testing packages) - Weekly schedule with sensible defaults Additional deliverables: - CI/CD documentation (docs/CI-CD.md) - Updated ROADMAP.md with completion status - Updated ARCHITECTURE.md with CI/CD reference --- .github/dependabot.yml | 151 +++++++++++ .github/workflows/angular.yml | 173 +++++++++++++ .github/workflows/deploy.yml | 279 ++++++++++++++++++++ .github/workflows/dotnet.yml | 149 +++++++++++ .github/workflows/infrastructure.yml | 298 +++++++++++++++++++++ docs/ARCHITECTURE.md | 6 +- docs/CI-CD.md | 374 +++++++++++++++++++++++++++ docs/ROADMAP.md | 29 ++- 8 files changed, 1449 insertions(+), 10 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/angular.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/dotnet.yml create mode 100644 .github/workflows/infrastructure.yml create mode 100644 docs/CI-CD.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b99bd21 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,151 @@ +# ============================================================================= +# Luminous - Dependabot Configuration +# ============================================================================= +# Configures automated dependency updates for all project ecosystems. +# +# Phase 0.5.5: Set up Dependabot for dependency updates +# TOGAF Principle: TP-4 - Infrastructure as Code +# ============================================================================= + +version: 2 + +registries: + nuget-org: + type: nuget-feed + url: https://api.nuget.org/v3/index.json + +updates: + # ============================================================================= + # .NET Dependencies (NuGet) + # ============================================================================= + - package-ecosystem: "nuget" + directory: "/" + registries: + - nuget-org + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 10 + commit-message: + prefix: "deps(nuget)" + labels: + - "dependencies" + - "nuget" + - ".net" + reviewers: + - "trickpatty" + groups: + # Group Microsoft packages together + microsoft: + patterns: + - "Microsoft.*" + - "System.*" + update-types: + - "minor" + - "patch" + # Group testing packages together + testing: + patterns: + - "xunit*" + - "Moq*" + - "FluentAssertions*" + - "NSubstitute*" + # Group Azure packages together + azure: + patterns: + - "Azure.*" + - "Microsoft.Azure.*" + + # ============================================================================= + # Angular/Node.js Dependencies (npm) + # ============================================================================= + - package-ecosystem: "npm" + directory: "/clients/web" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 10 + commit-message: + prefix: "deps(npm)" + labels: + - "dependencies" + - "npm" + - "angular" + reviewers: + - "trickpatty" + groups: + # Group Angular packages together + angular: + patterns: + - "@angular/*" + - "@angular-devkit/*" + update-types: + - "minor" + - "patch" + # Group Tailwind packages together + tailwind: + patterns: + - "tailwindcss" + - "@tailwindcss/*" + - "autoprefixer" + - "postcss" + # Group testing packages together + testing: + patterns: + - "jasmine*" + - "karma*" + - "@types/jasmine" + ignore: + # Ignore major Angular updates (require manual migration) + - dependency-name: "@angular/*" + update-types: ["version-update:semver-major"] + - dependency-name: "@angular-devkit/*" + update-types: ["version-update:semver-major"] + + # ============================================================================= + # GitHub Actions Dependencies + # ============================================================================= + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 5 + commit-message: + prefix: "deps(actions)" + labels: + - "dependencies" + - "github-actions" + - "ci" + reviewers: + - "trickpatty" + groups: + # Group all GitHub Actions together + github-actions: + patterns: + - "*" + + # ============================================================================= + # Docker Dependencies + # ============================================================================= + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 5 + commit-message: + prefix: "deps(docker)" + labels: + - "dependencies" + - "docker" + reviewers: + - "trickpatty" diff --git a/.github/workflows/angular.yml b/.github/workflows/angular.yml new file mode 100644 index 0000000..ad5e62c --- /dev/null +++ b/.github/workflows/angular.yml @@ -0,0 +1,173 @@ +# ============================================================================= +# Luminous - Angular Build and Test Workflow +# ============================================================================= +# Builds and tests the Angular web application on every push and pull request. +# +# Phase 0.5.2: GitHub Actions for Angular build and test +# TOGAF Principle: TP-4 - Infrastructure as Code +# ============================================================================= + +name: Angular Build and Test + +on: + push: + branches: [main, develop] + paths: + - 'clients/web/**' + - '.github/workflows/angular.yml' + pull_request: + branches: [main, develop] + paths: + - 'clients/web/**' + - '.github/workflows/angular.yml' + workflow_dispatch: + +env: + NODE_VERSION: '20.x' + WORKING_DIRECTORY: ./clients/web + +defaults: + run: + working-directory: ./clients/web + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + + permissions: + contents: read + checks: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: '${{ env.WORKING_DIRECTORY }}/package-lock.json' + + - name: Install dependencies + run: npm ci + + - name: Run linting + run: npm run lint --if-present + continue-on-error: true + + - name: Run type checking + run: npm run typecheck + + - name: Run unit tests + run: npm run test:ci + + - name: Build for production + run: npm run build:prod + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + if: success() + with: + name: angular-build-production + path: ${{ env.WORKING_DIRECTORY }}/dist/web + retention-days: 7 + + build-staging: + name: Build (Staging) + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' || github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: '${{ env.WORKING_DIRECTORY }}/package-lock.json' + + - name: Install dependencies + run: npm ci + + - name: Build for staging + run: npm run build:staging + + - name: Upload staging build artifacts + uses: actions/upload-artifact@v4 + if: success() + with: + name: angular-build-staging + path: ${{ env.WORKING_DIRECTORY }}/dist/web + retention-days: 7 + + security-audit: + name: Security Audit + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + permissions: + contents: read + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: '${{ env.WORKING_DIRECTORY }}/package-lock.json' + + - name: Run npm audit + run: | + npm audit --audit-level=high 2>&1 | tee npm-audit.txt || true + if grep -q "high\|critical" npm-audit.txt; then + echo "::warning::Security vulnerabilities detected. Review npm-audit.txt for details." + fi + continue-on-error: true + + - name: Upload audit results + uses: actions/upload-artifact@v4 + with: + name: npm-audit-results + path: ${{ env.WORKING_DIRECTORY }}/npm-audit.txt + retention-days: 30 + + bundle-analysis: + name: Bundle Analysis + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: '${{ env.WORKING_DIRECTORY }}/package-lock.json' + + - name: Install dependencies + run: npm ci + + - name: Build with stats + run: npm run build:prod -- --stats-json + + - name: Analyze bundle size + run: | + if [ -f "dist/web/browser/stats.json" ]; then + echo "## Bundle Size Analysis" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Production build completed successfully." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + du -sh dist/web/browser/ | awk '{print "Total bundle size: " $1}' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..33c0e1f --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,279 @@ +# ============================================================================= +# Luminous - Application Deployment Workflow +# ============================================================================= +# Deploys the .NET API and Angular web application to Azure. +# Triggered after successful builds on main/develop branches. +# +# Phase 0.5.4: Configure environment-specific deployments +# TOGAF Principle: TP-4 - Infrastructure as Code +# ============================================================================= + +name: Deploy Application + +on: + push: + branches: [main] + paths: + - 'src/**' + - 'clients/web/**' + workflow_dispatch: + inputs: + environment: + description: 'Target environment for deployment' + required: true + default: 'dev' + type: choice + options: + - dev + - staging + - prod + deploy_api: + description: 'Deploy .NET API' + required: true + default: true + type: boolean + deploy_web: + description: 'Deploy Angular Web App' + required: true + default: true + type: boolean + +env: + DOTNET_VERSION: '9.0.x' + NODE_VERSION: '20.x' + AZURE_LOCATION: 'eastus2' + +permissions: + id-token: write + contents: read + +jobs: + # ============================================================================= + # Build Jobs + # ============================================================================= + + build-api: + name: Build .NET API + runs-on: ubuntu-latest + if: github.event_name == 'push' || github.event.inputs.deploy_api == 'true' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Restore dependencies + run: dotnet restore Luminous.sln + + - name: Build and publish API + run: | + dotnet publish src/Luminous.Api/Luminous.Api.csproj \ + --configuration Release \ + --output ./publish/api \ + --no-restore + + - name: Upload API artifact + uses: actions/upload-artifact@v4 + with: + name: api-package + path: ./publish/api + retention-days: 1 + + build-web: + name: Build Angular Web App + runs-on: ubuntu-latest + if: github.event_name == 'push' || github.event.inputs.deploy_web == 'true' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: 'clients/web/package-lock.json' + + - name: Install dependencies + working-directory: ./clients/web + run: npm ci + + - name: Build for production + working-directory: ./clients/web + run: npm run build:prod + + - name: Upload web artifact + uses: actions/upload-artifact@v4 + with: + name: web-package + path: ./clients/web/dist/web + retention-days: 1 + + # ============================================================================= + # Deploy to Dev Environment + # ============================================================================= + + deploy-dev: + name: Deploy to Dev + runs-on: ubuntu-latest + needs: [build-api, build-web] + if: | + always() && + !contains(needs.*.result, 'failure') && + !contains(needs.*.result, 'cancelled') && + (github.ref == 'refs/heads/main' || (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'dev')) + environment: dev + + steps: + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Download API artifact + if: needs.build-api.result == 'success' + uses: actions/download-artifact@v4 + with: + name: api-package + path: ./api-package + + - name: Deploy API to App Service + if: needs.build-api.result == 'success' + uses: azure/webapps-deploy@v3 + with: + app-name: app-lum-dev-api + package: ./api-package + + - name: Download web artifact + if: needs.build-web.result == 'success' + uses: actions/download-artifact@v4 + with: + name: web-package + path: ./web-package + + - name: Deploy Web to Static Web App + if: needs.build-web.result == 'success' + uses: azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APP_TOKEN_DEV }} + repo_token: ${{ secrets.GITHUB_TOKEN }} + action: 'upload' + app_location: './web-package' + skip_app_build: true + + # ============================================================================= + # Deploy to Staging Environment + # ============================================================================= + + deploy-staging: + name: Deploy to Staging + runs-on: ubuntu-latest + needs: [build-api, build-web] + if: | + always() && + !contains(needs.*.result, 'failure') && + !contains(needs.*.result, 'cancelled') && + github.event_name == 'workflow_dispatch' && + github.event.inputs.environment == 'staging' + environment: staging + + steps: + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Download API artifact + if: needs.build-api.result == 'success' + uses: actions/download-artifact@v4 + with: + name: api-package + path: ./api-package + + - name: Deploy API to App Service + if: needs.build-api.result == 'success' + uses: azure/webapps-deploy@v3 + with: + app-name: app-lum-staging-api + package: ./api-package + + - name: Download web artifact + if: needs.build-web.result == 'success' + uses: actions/download-artifact@v4 + with: + name: web-package + path: ./web-package + + - name: Deploy Web to Static Web App + if: needs.build-web.result == 'success' + uses: azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APP_TOKEN_STAGING }} + repo_token: ${{ secrets.GITHUB_TOKEN }} + action: 'upload' + app_location: './web-package' + skip_app_build: true + + # ============================================================================= + # Deploy to Production Environment + # ============================================================================= + + deploy-prod: + name: Deploy to Production + runs-on: ubuntu-latest + needs: [build-api, build-web] + if: | + always() && + !contains(needs.*.result, 'failure') && + !contains(needs.*.result, 'cancelled') && + github.event_name == 'workflow_dispatch' && + github.event.inputs.environment == 'prod' + environment: prod + + steps: + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Download API artifact + if: needs.build-api.result == 'success' + uses: actions/download-artifact@v4 + with: + name: api-package + path: ./api-package + + - name: Deploy API to App Service + if: needs.build-api.result == 'success' + uses: azure/webapps-deploy@v3 + with: + app-name: app-lum-prod-api + package: ./api-package + + - name: Download web artifact + if: needs.build-web.result == 'success' + uses: actions/download-artifact@v4 + with: + name: web-package + path: ./web-package + + - name: Deploy Web to Static Web App + if: needs.build-web.result == 'success' + uses: azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APP_TOKEN_PROD }} + repo_token: ${{ secrets.GITHUB_TOKEN }} + action: 'upload' + app_location: './web-package' + skip_app_build: true diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..519ddc1 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,149 @@ +# ============================================================================= +# Luminous - .NET Build and Test Workflow +# ============================================================================= +# Builds and tests the .NET solution on every push and pull request. +# +# Phase 0.5.1: GitHub Actions for .NET build and test +# TOGAF Principle: TP-4 - Infrastructure as Code +# ============================================================================= + +name: .NET Build and Test + +on: + push: + branches: [main, develop] + paths: + - 'src/**' + - 'tests/**' + - '*.sln' + - 'Directory.Build.props' + - 'Directory.Packages.props' + - 'global.json' + - '.github/workflows/dotnet.yml' + pull_request: + branches: [main, develop] + paths: + - 'src/**' + - 'tests/**' + - '*.sln' + - 'Directory.Build.props' + - 'Directory.Packages.props' + - 'global.json' + - '.github/workflows/dotnet.yml' + workflow_dispatch: + +env: + DOTNET_VERSION: '9.0.x' + DOTNET_NOLOGO: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + + permissions: + contents: read + checks: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + cache: true + cache-dependency-path: '**/packages.lock.json' + + - name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ${{ env.NUGET_PACKAGES }} + key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json', '**/Directory.Packages.props') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore dependencies + run: dotnet restore Luminous.sln + + - name: Build solution + run: dotnet build Luminous.sln --configuration Release --no-restore + + - name: Run tests + run: | + dotnet test Luminous.sln \ + --configuration Release \ + --no-build \ + --verbosity normal \ + --logger "trx;LogFileName=test-results.trx" \ + --collect:"XPlat Code Coverage" \ + --results-directory ./TestResults + + - name: Publish test results + uses: dorny/test-reporter@v1 + if: success() || failure() + with: + name: .NET Test Results + path: './TestResults/**/*.trx' + reporter: dotnet-trx + fail-on-error: true + + - name: Upload coverage reports + uses: codecov/codecov-action@v4 + if: success() + with: + files: ./TestResults/**/coverage.cobertura.xml + fail_ci_if_error: false + verbose: true + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + if: success() + with: + name: dotnet-build + path: | + src/**/bin/Release/** + !src/**/bin/Release/**/*.pdb + retention-days: 7 + + security-scan: + name: Security Scan + runs-on: ubuntu-latest + needs: build + if: github.event_name == 'pull_request' + + permissions: + contents: read + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Restore dependencies + run: dotnet restore Luminous.sln + + - name: Run security scan + run: | + dotnet list package --vulnerable --include-transitive 2>&1 | tee security-scan.txt + if grep -q "has the following vulnerable packages" security-scan.txt; then + echo "::warning::Vulnerable packages detected. Review security-scan.txt for details." + fi + + - name: Upload security scan results + uses: actions/upload-artifact@v4 + with: + name: security-scan-results + path: security-scan.txt + retention-days: 30 diff --git a/.github/workflows/infrastructure.yml b/.github/workflows/infrastructure.yml new file mode 100644 index 0000000..e829500 --- /dev/null +++ b/.github/workflows/infrastructure.yml @@ -0,0 +1,298 @@ +# ============================================================================= +# Luminous - Infrastructure Deployment Workflow +# ============================================================================= +# Validates and deploys Azure infrastructure using Bicep and Azure Verified +# Modules (AVMs). Supports environment-specific deployments (dev, staging, prod). +# +# Phase 0.5.3: GitHub Actions for Bicep deployment +# Phase 0.5.4: Configure environment-specific deployments +# TOGAF Principle: TP-4 - Infrastructure as Code +# ADR-007: Bicep with AVMs for IaC +# ============================================================================= + +name: Infrastructure Deployment + +on: + push: + branches: [main, develop] + paths: + - 'infra/**' + - '.github/workflows/infrastructure.yml' + pull_request: + branches: [main, develop] + paths: + - 'infra/**' + - '.github/workflows/infrastructure.yml' + workflow_dispatch: + inputs: + environment: + description: 'Target environment for deployment' + required: true + default: 'dev' + type: choice + options: + - dev + - staging + - prod + deploy: + description: 'Deploy infrastructure (not just validate)' + required: true + default: false + type: boolean + +env: + AZURE_LOCATION: 'eastus2' + +permissions: + id-token: write + contents: read + pull-requests: write + +jobs: + validate: + name: Validate Bicep + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Bicep CLI + run: | + curl -Lo bicep https://github.com/Azure/bicep/releases/latest/download/bicep-linux-x64 + chmod +x bicep + sudo mv bicep /usr/local/bin/bicep + bicep --version + + - name: Lint Bicep files + run: | + echo "## Bicep Linting Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Build main.bicep to validate syntax and lint + bicep build infra/bicep/main.bicep --stdout > /dev/null 2>&1 && \ + echo "main.bicep: Valid" >> $GITHUB_STEP_SUMMARY || \ + (echo "main.bicep: Failed" >> $GITHUB_STEP_SUMMARY && exit 1) + + - name: Validate Bicep templates + run: | + # Validate all parameter files + for param in infra/bicep/parameters/*.bicepparam; do + echo "Validating: $param" + bicep build-params "$param" --stdout > /dev/null 2>&1 || exit 1 + done + + echo "" >> $GITHUB_STEP_SUMMARY + echo "All Bicep templates validated successfully." >> $GITHUB_STEP_SUMMARY + + what-if-dev: + name: What-If (Dev) + runs-on: ubuntu-latest + needs: validate + if: github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'dev') + environment: dev + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Run What-If (Dev) + uses: azure/arm-deploy@v2 + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + region: ${{ env.AZURE_LOCATION }} + template: infra/bicep/main.bicep + parameters: infra/bicep/parameters/dev.bicepparam + deploymentMode: Incremental + additionalArguments: --what-if + + what-if-staging: + name: What-If (Staging) + runs-on: ubuntu-latest + needs: validate + if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'staging' + environment: staging + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Run What-If (Staging) + uses: azure/arm-deploy@v2 + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + region: ${{ env.AZURE_LOCATION }} + template: infra/bicep/main.bicep + parameters: infra/bicep/parameters/staging.bicepparam + deploymentMode: Incremental + additionalArguments: --what-if + + what-if-prod: + name: What-If (Prod) + runs-on: ubuntu-latest + needs: validate + if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'prod' + environment: prod + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Run What-If (Prod) + uses: azure/arm-deploy@v2 + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + region: ${{ env.AZURE_LOCATION }} + template: infra/bicep/main.bicep + parameters: infra/bicep/parameters/prod.bicepparam + deploymentMode: Incremental + additionalArguments: --what-if + + deploy-dev: + name: Deploy (Dev) + runs-on: ubuntu-latest + needs: [validate, what-if-dev] + if: | + (github.ref == 'refs/heads/develop' && github.event_name == 'push') || + (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'dev' && github.event.inputs.deploy == 'true') + environment: dev + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Deploy to Dev + uses: azure/arm-deploy@v2 + id: deploy + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + region: ${{ env.AZURE_LOCATION }} + template: infra/bicep/main.bicep + parameters: infra/bicep/parameters/dev.bicepparam + deploymentName: luminous-dev-${{ github.run_number }} + failOnStdErr: false + + - name: Output deployment results + run: | + echo "## Dev Deployment Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Resource | Value |" >> $GITHUB_STEP_SUMMARY + echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Resource Group | ${{ steps.deploy.outputs.resourceGroupName }} |" >> $GITHUB_STEP_SUMMARY + echo "| API URL | ${{ steps.deploy.outputs.appServiceUrl }} |" >> $GITHUB_STEP_SUMMARY + echo "| Static Web App | ${{ steps.deploy.outputs.staticWebAppUrl }} |" >> $GITHUB_STEP_SUMMARY + echo "| Cosmos DB | ${{ steps.deploy.outputs.cosmosDbAccountName }} |" >> $GITHUB_STEP_SUMMARY + + deploy-staging: + name: Deploy (Staging) + runs-on: ubuntu-latest + needs: [validate, what-if-staging] + if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'staging' && github.event.inputs.deploy == 'true' + environment: staging + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Deploy to Staging + uses: azure/arm-deploy@v2 + id: deploy + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + region: ${{ env.AZURE_LOCATION }} + template: infra/bicep/main.bicep + parameters: infra/bicep/parameters/staging.bicepparam + deploymentName: luminous-staging-${{ github.run_number }} + failOnStdErr: false + + - name: Output deployment results + run: | + echo "## Staging Deployment Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Resource | Value |" >> $GITHUB_STEP_SUMMARY + echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Resource Group | ${{ steps.deploy.outputs.resourceGroupName }} |" >> $GITHUB_STEP_SUMMARY + echo "| API URL | ${{ steps.deploy.outputs.appServiceUrl }} |" >> $GITHUB_STEP_SUMMARY + echo "| Static Web App | ${{ steps.deploy.outputs.staticWebAppUrl }} |" >> $GITHUB_STEP_SUMMARY + echo "| Cosmos DB | ${{ steps.deploy.outputs.cosmosDbAccountName }} |" >> $GITHUB_STEP_SUMMARY + + deploy-prod: + name: Deploy (Prod) + runs-on: ubuntu-latest + needs: [validate, what-if-prod] + if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'prod' && github.event.inputs.deploy == 'true' + environment: prod + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Deploy to Prod + uses: azure/arm-deploy@v2 + id: deploy + with: + scope: subscription + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + region: ${{ env.AZURE_LOCATION }} + template: infra/bicep/main.bicep + parameters: infra/bicep/parameters/prod.bicepparam + deploymentName: luminous-prod-${{ github.run_number }} + failOnStdErr: false + + - name: Output deployment results + run: | + echo "## Production Deployment Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Resource | Value |" >> $GITHUB_STEP_SUMMARY + echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Resource Group | ${{ steps.deploy.outputs.resourceGroupName }} |" >> $GITHUB_STEP_SUMMARY + echo "| API URL | ${{ steps.deploy.outputs.appServiceUrl }} |" >> $GITHUB_STEP_SUMMARY + echo "| Static Web App | ${{ steps.deploy.outputs.staticWebAppUrl }} |" >> $GITHUB_STEP_SUMMARY + echo "| Cosmos DB | ${{ steps.deploy.outputs.cosmosDbAccountName }} |" >> $GITHUB_STEP_SUMMARY diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 85e45de..5b43430 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,7 +1,7 @@ # Luminous Architecture Document -> **Document Version:** 2.1.0 -> **Last Updated:** 2025-12-21 +> **Document Version:** 2.2.0 +> **Last Updated:** 2025-12-22 > **Status:** Draft > **TOGAF Phase:** Phase B-D (Architecture Development) @@ -716,6 +716,7 @@ services: - [Development Roadmap](./ROADMAP.md) - [Architecture Decision Records](./adr/) - [Azure Infrastructure](./AZURE-INFRASTRUCTURE.md) +- [CI/CD Pipeline](./CI-CD.md) - [CLAUDE.md (Development Guidelines)](../CLAUDE.md) --- @@ -727,3 +728,4 @@ services: | 1.0.0 | 2025-12-21 | Luminous Team | Initial architecture document | | 2.0.0 | 2025-12-21 | Luminous Team | Updated for Azure/.NET/Angular stack, multi-tenancy | | 2.1.0 | 2025-12-21 | Luminous Team | Migrate from Guid to NanoId for unique identifiers | +| 2.2.0 | 2025-12-22 | Luminous Team | Added CI/CD documentation reference | diff --git a/docs/CI-CD.md b/docs/CI-CD.md new file mode 100644 index 0000000..538c83f --- /dev/null +++ b/docs/CI-CD.md @@ -0,0 +1,374 @@ +# Luminous CI/CD Documentation + +> **Document Version:** 1.0.0 +> **Last Updated:** 2025-12-22 +> **Status:** Active +> **TOGAF Phase:** Phase G (Implementation Governance) + +--- + +## Table of Contents + +1. [Overview](#overview) +2. [GitHub Actions Workflows](#github-actions-workflows) +3. [Environment Configuration](#environment-configuration) +4. [Secrets Management](#secrets-management) +5. [Deployment Process](#deployment-process) +6. [Dependabot Configuration](#dependabot-configuration) +7. [Troubleshooting](#troubleshooting) + +--- + +## Overview + +Luminous uses GitHub Actions for continuous integration and deployment. The CI/CD pipeline follows TOGAF principles and ensures code quality, security, and reliable deployments. + +### Pipeline Architecture + +``` ++------------------+ +------------------+ +------------------+ +| Push/PR Event |---->| Build & Test |---->| Deploy (Auto) | ++------------------+ +------------------+ +------------------+ + | | + v v + +---------------+ +---------------+ + | .NET Tests | | Dev | + | Angular Tests | | Staging | + | Bicep Lint | | Production | + +---------------+ +---------------+ +``` + +### Key Principles + +| Principle | Implementation | +|-----------|----------------| +| **Shift Left** | Run tests and security scans on every PR | +| **Environment Parity** | Same artifacts deployed to all environments | +| **Infrastructure as Code** | Bicep templates for all Azure resources | +| **Automated Updates** | Dependabot for dependency management | + +--- + +## GitHub Actions Workflows + +### 1. .NET Build and Test (`dotnet.yml`) + +**Triggers:** +- Push to `main` or `develop` branches (when `.NET` files change) +- Pull requests targeting `main` or `develop` +- Manual workflow dispatch + +**Jobs:** + +| Job | Description | Runs On | +|-----|-------------|---------| +| `build` | Restore, build, test, and collect coverage | Every trigger | +| `security-scan` | Check for vulnerable NuGet packages | Pull requests only | + +**Artifacts:** +- `dotnet-build`: Compiled binaries (7-day retention) +- `security-scan-results`: Vulnerability report (30-day retention) + +**Example Usage:** +```bash +# Trigger manually via GitHub CLI +gh workflow run dotnet.yml +``` + +### 2. Angular Build and Test (`angular.yml`) + +**Triggers:** +- Push to `main` or `develop` branches (when `clients/web/**` changes) +- Pull requests targeting `main` or `develop` +- Manual workflow dispatch + +**Jobs:** + +| Job | Description | Runs On | +|-----|-------------|---------| +| `build` | Lint, typecheck, test, and build production | Every trigger | +| `build-staging` | Build staging configuration | Develop branch and PRs | +| `security-audit` | Run npm audit for vulnerabilities | Pull requests only | +| `bundle-analysis` | Analyze bundle size | Pull requests only | + +**Artifacts:** +- `angular-build-production`: Production build (7-day retention) +- `angular-build-staging`: Staging build (7-day retention) +- `npm-audit-results`: Security audit report (30-day retention) + +### 3. Infrastructure Deployment (`infrastructure.yml`) + +**Triggers:** +- Push to `main` or `develop` branches (when `infra/**` changes) +- Pull requests targeting `main` or `develop` +- Manual workflow dispatch with environment selection + +**Jobs:** + +| Job | Description | Runs On | +|-----|-------------|---------| +| `validate` | Lint and validate Bicep templates | Every trigger | +| `what-if-{env}` | Preview infrastructure changes | Based on environment | +| `deploy-{env}` | Deploy infrastructure to Azure | Auto or manual | + +**Manual Deployment:** +```bash +# Deploy to dev environment +gh workflow run infrastructure.yml \ + -f environment=dev \ + -f deploy=true + +# Deploy to production (requires approval) +gh workflow run infrastructure.yml \ + -f environment=prod \ + -f deploy=true +``` + +### 4. Application Deployment (`deploy.yml`) + +**Triggers:** +- Push to `main` branch (when `src/**` or `clients/web/**` changes) +- Manual workflow dispatch with environment and component selection + +**Jobs:** + +| Job | Description | Runs On | +|-----|-------------|---------| +| `build-api` | Build and package .NET API | When API deployment is requested | +| `build-web` | Build Angular web application | When web deployment is requested | +| `deploy-{env}` | Deploy to target environment | Based on trigger and environment | + +**Manual Deployment:** +```bash +# Deploy everything to staging +gh workflow run deploy.yml \ + -f environment=staging \ + -f deploy_api=true \ + -f deploy_web=true + +# Deploy only API to production +gh workflow run deploy.yml \ + -f environment=prod \ + -f deploy_api=true \ + -f deploy_web=false +``` + +--- + +## Environment Configuration + +### GitHub Environments + +Three environments are configured in the GitHub repository: + +| Environment | Protection Rules | Deployment | +|-------------|------------------|------------| +| `dev` | None | Automatic on main push | +| `staging` | None | Manual trigger | +| `prod` | Required reviewers | Manual trigger with approval | + +### Azure Resources by Environment + +| Resource | Dev | Staging | Prod | +|----------|-----|---------|------| +| **App Service** | `app-lum-dev-api` | `app-lum-staging-api` | `app-lum-prod-api` | +| **Static Web App** | `stapp-lum-dev` | `stapp-lum-staging` | `stapp-lum-prod` | +| **Cosmos DB** | Serverless | Provisioned | Provisioned | +| **Redis** | Basic C0 | Basic C0 | Standard C1 | +| **App Service Plan** | B1 | B1 | P1v3 | + +--- + +## Secrets Management + +### Required GitHub Secrets + +Configure these secrets in your GitHub repository settings: + +#### Azure Authentication (OIDC - Recommended) + +| Secret | Description | +|--------|-------------| +| `AZURE_CLIENT_ID` | Azure AD application (client) ID | +| `AZURE_TENANT_ID` | Azure AD tenant ID | +| `AZURE_SUBSCRIPTION_ID` | Azure subscription ID | + +#### Static Web App Tokens + +| Secret | Description | +|--------|-------------| +| `AZURE_STATIC_WEB_APP_TOKEN_DEV` | Deployment token for dev SWA | +| `AZURE_STATIC_WEB_APP_TOKEN_STAGING` | Deployment token for staging SWA | +| `AZURE_STATIC_WEB_APP_TOKEN_PROD` | Deployment token for prod SWA | + +### Setting Up Azure OIDC Authentication + +1. **Create Azure AD App Registration:** + ```bash + az ad app create --display-name "Luminous-GitHub-OIDC" + ``` + +2. **Configure Federated Credential:** + ```bash + az ad app federated-credential create \ + --id \ + --parameters '{ + "name": "github-main", + "issuer": "https://token.actions.githubusercontent.com", + "subject": "repo:trickpatty/Luminous:ref:refs/heads/main", + "audiences": ["api://AzureADTokenExchange"] + }' + ``` + +3. **Assign Roles:** + ```bash + # Contributor role for deployments + az role assignment create \ + --assignee \ + --role "Contributor" \ + --scope /subscriptions/ + ``` + +--- + +## Deployment Process + +### Automated Deployments + +``` +main branch push + | + v ++------------------+ +| .NET Build/Test |---> Pass ---> Deploy API to Dev ++------------------+ + | + v ++------------------+ +| Angular Build |---> Pass ---> Deploy Web to Dev ++------------------+ +``` + +### Manual Deployments + +1. Navigate to **Actions** tab in GitHub +2. Select the workflow (e.g., "Deploy Application") +3. Click **Run workflow** +4. Select target environment and components +5. Click **Run workflow** button + +### Production Deployment Checklist + +- [ ] All tests pass on `main` branch +- [ ] Security scans show no critical vulnerabilities +- [ ] Staging deployment verified +- [ ] Change log updated +- [ ] Required reviewers approved + +--- + +## Dependabot Configuration + +### Update Schedule + +All dependency updates are scheduled for **Monday at 6:00 AM ET**. + +### Ecosystem Configuration + +| Ecosystem | Directory | Groups | +|-----------|-----------|--------| +| **NuGet** | `/` | Microsoft, Testing, Azure | +| **npm** | `/clients/web` | Angular, Tailwind, Testing | +| **GitHub Actions** | `/` | All actions | +| **Docker** | `/` | All images | + +### Handling Updates + +1. **Review PR:** Check the changelog and breaking changes +2. **Run Tests:** Ensure all CI checks pass +3. **Merge:** Use squash merge to keep history clean + +### Ignoring Updates + +To ignore specific updates, add to `.github/dependabot.yml`: + +```yaml +ignore: + - dependency-name: "package-name" + update-types: ["version-update:semver-major"] +``` + +--- + +## Troubleshooting + +### Common Issues + +#### 1. Azure Login Fails + +**Error:** `AADSTS700024: Client assertion is not within its valid time range` + +**Solution:** Ensure GitHub Actions runner time is synchronized. Try re-running the job. + +#### 2. Bicep Deployment Fails + +**Error:** `InvalidTemplateDeployment` + +**Solution:** +1. Run `bicep build` locally to validate syntax +2. Check Azure resource quotas +3. Verify parameter file matches template + +```bash +# Validate locally +bicep build infra/bicep/main.bicep +bicep build-params infra/bicep/parameters/dev.bicepparam +``` + +#### 3. Static Web App Deployment Fails + +**Error:** `Failed to deploy static web app` + +**Solution:** +1. Verify the deployment token is valid +2. Check that the build output matches expected structure +3. Regenerate token if expired + +#### 4. .NET Tests Timeout + +**Error:** `The operation was canceled` + +**Solution:** +1. Check for infinite loops or long-running tests +2. Increase timeout in workflow if needed +3. Split test projects for parallel execution + +### Viewing Logs + +```bash +# View recent workflow runs +gh run list --workflow=dotnet.yml + +# View specific run logs +gh run view --log + +# Download artifacts +gh run download +``` + +--- + +## Related Documents + +- [Architecture Overview](./ARCHITECTURE.md) +- [Azure Infrastructure](./AZURE-INFRASTRUCTURE.md) +- [Development Setup](./DEVELOPMENT.md) +- [CLAUDE.md (Development Guidelines)](../CLAUDE.md) + +--- + +## Document History + +| Version | Date | Author | Changes | +|---------|------|--------|---------| +| 1.0.0 | 2025-12-22 | Luminous Team | Initial CI/CD documentation | diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index a1960c2..62d4ef4 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,6 +1,6 @@ # Luminous Development Roadmap -> **Document Version:** 2.4.0 +> **Document Version:** 2.5.0 > **Last Updated:** 2025-12-22 > **Status:** Active > **TOGAF Phase:** Phase E/F (Opportunities, Solutions & Migration Planning) @@ -102,7 +102,7 @@ Phase 6: Intelligence & Ecosystem | Phase | Name | Focus | Key Deliverables | Status | |-------|------|-------|------------------|--------| -| **0** | Foundation | Infrastructure | Azure IaC, .NET solution, Angular shell, Passwordless Auth, Local Dev | 🟡 In Progress (0.1, 0.2, 0.3, 0.4 Complete) | +| **0** | Foundation | Infrastructure | Azure IaC, .NET solution, Angular shell, Passwordless Auth, Local Dev, CI/CD | 🟡 In Progress (0.1-0.5 Complete) | | **1** | Core Platform | Multi-tenancy | Family sign-up, device linking, CosmosDB, web MVP | ⬜ Not Started | | **2** | Display & Calendar | Calendar visibility | Display app, calendar integration, SignalR sync | ⬜ Not Started | | **3** | Native Mobile | Mobile apps | iOS (Swift), Android (Kotlin), push notifications | ⬜ Not Started | @@ -211,13 +211,25 @@ Establish the Azure infrastructure, .NET backend, Angular frontend, and developm - [x] Authorization policies (FamilyMember, FamilyAdmin, FamilyOwner) - [x] Development authentication endpoint (POST /api/devauth/token) -#### 0.5 CI/CD Pipeline +#### 0.5 CI/CD Pipeline ✅ COMPLETED -- [ ] **0.5.1** GitHub Actions for .NET build and test -- [ ] **0.5.2** GitHub Actions for Angular build and test -- [ ] **0.5.3** GitHub Actions for Bicep deployment -- [ ] **0.5.4** Configure environment-specific deployments -- [ ] **0.5.5** Set up Dependabot for dependency updates +- [x] **0.5.1** GitHub Actions for .NET build and test + - *Implemented: .github/workflows/dotnet.yml with build, test, coverage, and security scanning* +- [x] **0.5.2** GitHub Actions for Angular build and test + - *Implemented: .github/workflows/angular.yml with lint, typecheck, test, build, and security audit* +- [x] **0.5.3** GitHub Actions for Bicep deployment + - *Implemented: .github/workflows/infrastructure.yml with validate, what-if, and deploy stages* +- [x] **0.5.4** Configure environment-specific deployments + - *Implemented: .github/workflows/deploy.yml with dev/staging/prod environments, Azure OIDC auth* +- [x] **0.5.5** Set up Dependabot for dependency updates + - *Implemented: .github/dependabot.yml for NuGet, npm, GitHub Actions, and Docker ecosystems* + +**Additional deliverables:** +- [x] CI/CD documentation (docs/CI-CD.md) +- [x] Environment-specific deployment workflows with approval gates +- [x] Security scanning for .NET (vulnerable packages) and npm (audit) +- [x] Artifact management for build outputs +- [x] Bundle size analysis for Angular builds #### 0.6 Documentation @@ -670,3 +682,4 @@ These can be developed in parallel after Phase 0: | 2.2.0 | 2025-12-21 | Luminous Team | Phase 0.2 .NET Solution Structure completed | | 2.3.0 | 2025-12-21 | Luminous Team | Phase 0.3 Angular Web Application completed | | 2.4.0 | 2025-12-22 | Luminous Team | Phase 0.4 Local Development Environment completed | +| 2.5.0 | 2025-12-22 | Luminous Team | Phase 0.5 CI/CD Pipeline completed |