feat: Add comprehensive emergency deployment procedures documentation #52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Deploy Documentation | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: 'Environment to deploy to' | |
| required: false | |
| default: 'staging' | |
| type: choice | |
| options: | |
| - staging | |
| - production | |
| force_rebuild: | |
| description: 'Force rebuild Docker image (ignore cache)' | |
| required: false | |
| default: false | |
| type: boolean | |
| skip_tests: | |
| description: 'Skip documentation tests' | |
| required: false | |
| default: false | |
| type: boolean | |
| deploy_argocd: | |
| description: 'Deploy to ArgoCD (update ApplicationSet)' | |
| required: false | |
| default: true | |
| type: boolean | |
| push: | |
| branches: | |
| - main | |
| - develop | |
| - 'feature/**' | |
| tags: | |
| - "v*" | |
| paths: | |
| - 'docs/**' | |
| - 'mkdocs.yml' | |
| - 'requirements.txt' | |
| - 'Dockerfile' | |
| - 'charts/**' | |
| - '.github/workflows/**' | |
| pull_request: | |
| branches: [ main, develop ] | |
| paths: | |
| - 'docs/**' | |
| - 'mkdocs.yml' | |
| - 'requirements.txt' | |
| - 'Dockerfile' | |
| - 'charts/**' | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: itlusions/identity-docs | |
| jobs: | |
| # Test and validate documentation | |
| test: | |
| runs-on: ubuntu-latest | |
| if: ${{ !inputs.skip_tests }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.11' | |
| - name: Cache Python dependencies | |
| uses: actions/cache@v3 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip- | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| - name: Test MkDocs build | |
| run: | | |
| mkdocs build --strict | |
| - name: Check links (if linkchecker available) | |
| run: | | |
| pip install linkchecker || true | |
| if command -v linkchecker &> /dev/null; then | |
| mkdocs serve & | |
| sleep 5 | |
| linkchecker http://127.0.0.1:8000 --check-extern || true | |
| pkill -f mkdocs | |
| fi | |
| # Lint Helm chart | |
| helm-lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Helm | |
| uses: azure/setup-helm@v3 | |
| with: | |
| version: '3.12.0' | |
| - name: Lint Helm chart | |
| run: | | |
| helm lint charts/identity-docs --strict | |
| - name: Validate Helm templates (disable schema validation) | |
| run: | | |
| helm template identity-docs charts/identity-docs \ | |
| --validate=false \ | |
| --dry-run > /dev/null | |
| - name: Generate manifests without validation | |
| run: | | |
| helm template identity-docs charts/identity-docs \ | |
| --validate=false > manifests-test.yaml | |
| - name: Validate Kubernetes manifests with kubeval | |
| run: | | |
| # Install kubeval for validation | |
| curl -L https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz | tar xz | |
| sudo mv kubeval /usr/local/bin | |
| # Validate with missing schemas ignored and skip Traefik CRDs | |
| kubeval manifests-test.yaml \ | |
| --ignore-missing-schemas \ | |
| --skip-kinds IngressRoute,Middleware,ServiceMonitor | |
| # Build and push Docker image | |
| build: | |
| needs: [helm-lint] | |
| runs-on: ubuntu-latest | |
| if: ${{ always() && (needs.helm-lint.result == 'success') && (!inputs.skip_tests || needs.test.result == 'success' || needs.test.result == 'skipped') }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| security-events: write | |
| outputs: | |
| image-digest: ${{ steps.build.outputs.digest }} | |
| image-tags: ${{ steps.meta.outputs.tags }} | |
| image-main-tag: ${{ steps.meta.outputs.tags }} # Will use the first tag for scanning | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.11' | |
| - name: Set up Helm | |
| uses: azure/setup-helm@v3 | |
| with: | |
| version: '3.12.0' | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to Container Registry | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=sha,prefix=sha-,format=short | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| flavor: | | |
| latest=auto | |
| - name: Build and push Docker image | |
| id: build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| push: ${{ startsWith(github.ref, 'refs/tags/v') }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: ${{ (inputs.force_rebuild == true) && 'type=registry' || 'type=gha' }} | |
| cache-to: type=gha,mode=max | |
| platforms: linux/amd64,linux/arm64 | |
| no-cache: ${{ inputs.force_rebuild == true }} | |
| - name: Build documentation site | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| mkdocs build | |
| - name: Upload documentation artifacts | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: identity-docs-site | |
| path: site/ | |
| if-no-files-found: error | |
| retention-days: 30 | |
| overwrite: true | |
| - name: Upload Helm chart artifacts | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: identity-docs-helm | |
| path: charts/identity-docs/ | |
| if-no-files-found: error | |
| retention-days: 30 | |
| overwrite: true | |
| # Security scan - separate job to run after image is pushed | |
| security-scan: | |
| needs: [build] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| permissions: | |
| contents: read | |
| packages: read | |
| security-events: write | |
| steps: | |
| - name: Log in to Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Wait for image propagation | |
| run: | | |
| echo "Waiting for image to be available in registry..." | |
| sleep 30 | |
| - name: Determine scan target | |
| id: scan-target | |
| run: | | |
| # Try multiple tag formats to find the image | |
| POSSIBLE_TAGS=( | |
| "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" | |
| "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main" | |
| "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main-${{ github.sha }}" | |
| "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" | |
| ) | |
| echo "Checking available tags in registry..." | |
| curl -u "${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}" \ | |
| "https://ghcr.io/v2/${{ env.IMAGE_NAME }}/tags/list" | jq '.' || echo "Failed to list tags" | |
| SCAN_IMAGE="" | |
| for tag in "${POSSIBLE_TAGS[@]}"; do | |
| echo "Checking if $tag exists..." | |
| if docker manifest inspect "$tag" > /dev/null 2>&1; then | |
| SCAN_IMAGE="$tag" | |
| echo "Found working tag: $tag" | |
| break | |
| fi | |
| done | |
| if [[ -z "$SCAN_IMAGE" ]]; then | |
| echo "No accessible image found. Using first tag from metadata." | |
| SCAN_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main-${{ github.sha }}" | |
| fi | |
| echo "scan-image=${SCAN_IMAGE}" >> $GITHUB_OUTPUT | |
| echo "Final scan target: ${SCAN_IMAGE}" | |
| - name: Verify image exists (with fallback) | |
| id: verify-image | |
| continue-on-error: true | |
| run: | | |
| echo "Verifying image: ${{ steps.scan-target.outputs.scan-image }}" | |
| if docker manifest inspect "${{ steps.scan-target.outputs.scan-image }}"; then | |
| echo "Image verification successful" | |
| echo "image-verified=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Image verification failed" | |
| echo "image-verified=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Run Trivy vulnerability scanner | |
| id: trivy-scan | |
| if: steps.verify-image.outputs.image-verified == 'true' | |
| continue-on-error: true | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ steps.scan-target.outputs.scan-image }} | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| severity: 'CRITICAL,HIGH' | |
| ignore-unfixed: true | |
| skip-files: '/var/lib/dpkg/status' | |
| skip-dirs: '/tmp,/var/cache' | |
| timeout: '10m' | |
| - name: Handle scan skip | |
| if: steps.verify-image.outputs.image-verified != 'true' | |
| run: | | |
| echo "Skipping security scan - image not verified" | |
| echo "This may happen if:" | |
| echo "1. The image push is still in progress" | |
| echo "2. There's a delay in registry synchronization" | |
| echo "3. Authentication issues with the registry" | |
| echo "" | |
| echo "The deployment will continue, but security scan results won't be available." | |
| - name: Check scan results | |
| run: | | |
| if [[ -f "trivy-results.sarif" ]]; then | |
| echo "Security scan completed successfully" | |
| echo "scan-success=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Security scan failed or produced no results" | |
| echo "scan-success=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload Trivy scan results to GitHub Security tab | |
| uses: github/codeql-action/upload-sarif@v2 | |
| if: always() && hashFiles('trivy-results.sarif') != '' | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| # Package and push Helm chart to GHCR | |
| helm-package: | |
| needs: [helm-lint] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Helm | |
| uses: azure/setup-helm@v3 | |
| with: | |
| version: '3.12.0' | |
| - name: Log in to Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Package Helm chart | |
| run: | | |
| echo "📦 Packaging Helm chart for version ${{ github.ref_name }}" | |
| # Create helm packages directory | |
| mkdir -p helm-packages | |
| # Package the chart with the version from the tag (remove 'v' prefix) | |
| CHART_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') | |
| echo "Chart version: $CHART_VERSION" | |
| # Update Chart.yaml with the release version | |
| sed -i "s/version: .*/version: $CHART_VERSION/" charts/identity-docs/Chart.yaml | |
| sed -i "s/appVersion: .*/appVersion: ${{ github.ref_name }}/" charts/identity-docs/Chart.yaml | |
| # Package the chart | |
| helm package charts/identity-docs \ | |
| --version "$CHART_VERSION" \ | |
| --app-version "${{ github.ref_name }}" \ | |
| --destination helm-packages/ | |
| echo "✅ Chart packaged successfully" | |
| ls -la helm-packages/ | |
| - name: Push Helm chart to GHCR | |
| run: | | |
| echo "🚀 Pushing Helm chart to GitHub Container Registry" | |
| # Enable OCI support for Helm | |
| export HELM_EXPERIMENTAL_OCI=1 | |
| # Chart version without 'v' prefix | |
| CHART_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') | |
| # Define the OCI repository URL | |
| CHART_REPO="${{ env.REGISTRY }}/itlusions/helm/identity-docs" | |
| echo "Chart repository: $CHART_REPO" | |
| echo "Chart version: $CHART_VERSION" | |
| # Find the packaged chart file | |
| CHART_FILE=$(find helm-packages -name "identity-docs-*.tgz" | head -1) | |
| if [ -z "$CHART_FILE" ]; then | |
| echo "❌ No chart file found in helm-packages/" | |
| ls -la helm-packages/ | |
| exit 1 | |
| fi | |
| echo "Chart file: $CHART_FILE" | |
| # Push the chart | |
| helm push "$CHART_FILE" "oci://${{ env.REGISTRY }}/itlusions/helm" | |
| echo "✅ Helm chart pushed successfully to $CHART_REPO:$CHART_VERSION" | |
| # Verify the push | |
| echo "🔍 Verifying chart availability..." | |
| helm show chart "oci://$CHART_REPO" --version "$CHART_VERSION" || echo "⚠️ Chart verification failed (may be due to propagation delay)" | |
| - name: Upload Helm package artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: helm-chart-package | |
| path: helm-packages/*.tgz | |
| if-no-files-found: error | |
| retention-days: 30 | |
| overwrite: true | |
| - name: Generate installation instructions | |
| run: | | |
| CHART_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') | |
| CHART_REPO="${{ env.REGISTRY }}/itlusions/helm/identity-docs" | |
| cat > helm-install-instructions.md << EOF | |
| # Identity Platform Helm Chart Installation | |
| ## Version: ${{ github.ref_name }} | |
| ### Install from GitHub Container Registry | |
| \`\`\`bash | |
| # Add the chart repository (one-time setup) | |
| helm registry login ${{ env.REGISTRY }} -u ${{ github.actor }} | |
| # Install the chart | |
| helm install identity-docs oci://$CHART_REPO --version $CHART_VERSION | |
| # Or upgrade existing installation | |
| helm upgrade identity-docs oci://$CHART_REPO --version $CHART_VERSION | |
| \`\`\` | |
| ### Install with custom values | |
| \`\`\`bash | |
| # Create your values file | |
| cat > my-values.yaml << EOL | |
| image: | |
| tag: "${{ github.ref_name }}" | |
| ingress: | |
| hosts: | |
| - host: docs.yourdomain.com | |
| paths: | |
| - path: /identity | |
| pathType: Prefix | |
| EOL | |
| # Install with custom values | |
| helm install identity-docs oci://$CHART_REPO --version $CHART_VERSION -f my-values.yaml | |
| \`\`\` | |
| ### Chart Information | |
| - **Chart Version**: $CHART_VERSION | |
| - **App Version**: ${{ github.ref_name }} | |
| - **Registry**: ${{ env.REGISTRY }} | |
| - **Repository**: itlusions/helm/identity-docs | |
| - **Full OCI URL**: oci://$CHART_REPO | |
| ### Available in GitHub Packages | |
| The chart is also available in the [GitHub Packages](https://github.com/ITlusions/ITL.identity.platform/pkgs/container/helm%2Fidentity-docs) for this repository. | |
| EOF | |
| echo "📋 Installation instructions generated" | |
| cat helm-install-instructions.md | |
| - name: Upload installation instructions | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: helm-install-instructions | |
| path: helm-install-instructions.md | |
| if-no-files-found: error | |
| retention-days: 90 | |
| overwrite: true | |
| # Deploy ArgoCD ApplicationSet for GitOps automation | |
| argocd-deploy: | |
| needs: [helm-package] | |
| runs-on: ubuntu-latest | |
| if: | | |
| (github.event_name == 'workflow_dispatch' && inputs.deploy_argocd) || | |
| (github.event_name != 'workflow_dispatch' && ( | |
| startsWith(github.ref, 'refs/tags/v') || | |
| github.ref == 'refs/heads/main' | |
| )) | |
| environment: | |
| name: ${{ github.event_name == 'workflow_dispatch' && inputs.environment || (startsWith(github.ref, 'refs/tags/v') && 'production' || 'staging') }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: ArgoCD Deployment Info | |
| run: | | |
| echo "🚀 ArgoCD ApplicationSet Deployment Starting" | |
| echo "Event: ${{ github.event_name }}" | |
| echo "Ref: ${{ github.ref }}" | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "Manual trigger - ArgoCD deployment: ${{ inputs.deploy_argocd }}" | |
| echo "Target environment: ${{ inputs.environment }}" | |
| else | |
| echo "Automatic trigger - ArgoCD deployment: enabled" | |
| fi | |
| echo "Target environment: ${{ github.event_name == 'workflow_dispatch' && inputs.environment || (startsWith(github.ref, 'refs/tags/v') && 'production' || 'staging') }}" | |
| - name: Set up kubectl | |
| uses: azure/setup-kubectl@v3 | |
| with: | |
| version: 'v1.28.0' | |
| - name: Configure kubectl for ArgoCD cluster | |
| run: | | |
| # Configure kubectl to connect to your Kubernetes cluster | |
| # This step depends on your cluster setup (AKS, EKS, GKE, etc.) | |
| echo "🔧 Configuring kubectl for ArgoCD cluster..." | |
| # For Azure AKS (uncomment and configure): | |
| # az aks get-credentials --resource-group ${{ secrets.AKS_RESOURCE_GROUP }} --name ${{ secrets.AKS_CLUSTER_NAME }} | |
| # For AWS EKS (uncomment and configure): | |
| # aws eks update-kubeconfig --region ${{ secrets.AWS_REGION }} --name ${{ secrets.EKS_CLUSTER_NAME }} | |
| # For direct kubeconfig (recommended for CI/CD): | |
| echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > ~/.kube/config | |
| chmod 600 ~/.kube/config | |
| # Verify connection | |
| kubectl cluster-info | |
| kubectl get namespaces | |
| env: | |
| KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }} | |
| - name: Update ApplicationSet with latest values | |
| run: | | |
| echo "🚀 Updating ArgoCD ApplicationSet for Identity Platform" | |
| # Determine environment and version based on trigger | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| ENVIRONMENT="${{ inputs.environment }}" | |
| if [[ "$ENVIRONMENT" == "production" ]]; then | |
| # For manual production deployment, use the current ref (should be a tag) | |
| if [[ "${{ github.ref }}" == refs/tags/v* ]]; then | |
| VERSION="${{ github.ref_name }}" | |
| else | |
| echo "⚠️ Warning: Manual production deployment from non-tag ref: ${{ github.ref }}" | |
| VERSION="${{ github.ref_name }}" | |
| fi | |
| else | |
| VERSION="main" | |
| fi | |
| elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then | |
| ENVIRONMENT="production" | |
| VERSION="${{ github.ref_name }}" | |
| else | |
| ENVIRONMENT="staging" | |
| VERSION="main" | |
| fi | |
| echo "🎯 Deployment Configuration:" | |
| echo " - Environment: $ENVIRONMENT" | |
| echo " - Image Tag: $VERSION" | |
| echo " - Chart Version: $(echo "$VERSION" | sed 's/^v//')" | |
| echo " - Trigger: ${{ github.event_name }}" | |
| echo " - Ref: ${{ github.ref }}" | |
| # Create a temporary ApplicationSet with updated values | |
| cp argocd/applicationset.yaml argocd/applicationset-updated.yaml | |
| # Update chart version for the target environment | |
| CHART_VERSION=$(echo "$VERSION" | sed 's/^v//') | |
| if [[ "$ENVIRONMENT" == "production" ]]; then | |
| echo "📝 Updating production environment configuration..." | |
| # Update production environment in the list | |
| sed -i "/- environment: production/,/- environment:/ { | |
| /chartVersion:/ s/chartVersion: \"[^\"]*\"/chartVersion: \"$CHART_VERSION\"/ | |
| /imageTag:/ s/imageTag: \"[^\"]*\"/imageTag: \"$VERSION\"/ | |
| }" argocd/applicationset-updated.yaml | |
| else | |
| echo "📝 Updating staging environment configuration..." | |
| # For staging, keep main branch but update chart version if it's a release | |
| if [[ "$VERSION" != "main" ]]; then | |
| sed -i "/- environment: staging/,/- environment:/ { | |
| /chartVersion:/ s/chartVersion: \"[^\"]*\"/chartVersion: \"$CHART_VERSION\"/ | |
| }" argocd/applicationset-updated.yaml | |
| fi | |
| fi | |
| echo "" | |
| echo "📄 Updated ApplicationSet preview (environment section):" | |
| echo "=======================================================" | |
| grep -A 15 -B 5 "environment: $ENVIRONMENT" argocd/applicationset-updated.yaml || echo "Environment section not found" | |
| # Store values for next steps | |
| echo "DEPLOYMENT_ENVIRONMENT=$ENVIRONMENT" >> $GITHUB_ENV | |
| echo "DEPLOYMENT_VERSION=$VERSION" >> $GITHUB_ENV | |
| echo "CHART_VERSION=$CHART_VERSION" >> $GITHUB_ENV | |
| - name: Apply ApplicationSet to ArgoCD | |
| run: | | |
| echo "📋 Applying ApplicationSet to ArgoCD cluster..." | |
| # Apply the AppProject first (if it doesn't exist) | |
| echo "🔐 Ensuring AppProject exists..." | |
| kubectl apply -f argocd/project.yaml --validate=false | |
| # Apply the ApplicationSet | |
| echo "🚀 Applying ApplicationSet..." | |
| kubectl apply -f argocd/applicationset-updated.yaml --validate=false | |
| # Wait for ApplicationSet to be processed | |
| echo "⏳ Waiting for ApplicationSet to generate Applications..." | |
| sleep 10 | |
| # Check generated Applications | |
| echo "📱 Checking generated Applications..." | |
| kubectl get applicationset -n argocd identity-docs-environments -o yaml || echo "ApplicationSet not found yet" | |
| kubectl get applications -n argocd -l app.kubernetes.io/name=identity-docs || echo "No Applications found yet" | |
| - name: Sync ArgoCD Applications (if available) | |
| run: | | |
| echo "🔄 Attempting to sync ArgoCD Applications..." | |
| # Check if ArgoCD CLI is available or install it | |
| if ! command -v argocd &> /dev/null; then | |
| echo "📥 Installing ArgoCD CLI..." | |
| curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64 | |
| chmod +x argocd | |
| sudo mv argocd /usr/local/bin/ | |
| fi | |
| # Configure ArgoCD CLI (if ArgoCD server is accessible) | |
| if [[ -n "${{ secrets.ARGOCD_SERVER }}" && -n "${{ secrets.ARGOCD_TOKEN }}" ]]; then | |
| echo "🔗 Connecting to ArgoCD server..." | |
| argocd login ${{ secrets.ARGOCD_SERVER }} \ | |
| --username admin \ | |
| --password ${{ secrets.ARGOCD_TOKEN }} \ | |
| --insecure | |
| # Sync the applications | |
| echo "🔄 Syncing Applications..." | |
| argocd app sync identity-docs-staging --timeout 300 || echo "Staging app sync failed or doesn't exist" | |
| if [[ "${{ startsWith(github.ref, 'refs/tags/v') }}" == "true" ]] || [[ "${{ inputs.environment }}" == "production" ]]; then | |
| argocd app sync identity-docs-production --timeout 300 || echo "Production app sync failed or doesn't exist" | |
| fi | |
| # Check application status | |
| echo "📊 Application Status:" | |
| argocd app list --selector app.kubernetes.io/name=identity-docs || echo "No applications found" | |
| else | |
| echo "ℹ️ ArgoCD server credentials not configured - skipping CLI sync" | |
| echo "Applications will be synced automatically by ArgoCD controllers" | |
| fi | |
| - name: Verify Deployment Status | |
| run: | | |
| echo "✅ Verifying ArgoCD deployment status..." | |
| # Check ApplicationSet status | |
| echo "📋 ApplicationSet Status:" | |
| kubectl get applicationset -n argocd identity-docs-environments -o wide || echo "ApplicationSet not found" | |
| # Check generated Applications | |
| echo "" | |
| echo "📱 Generated Applications:" | |
| kubectl get applications -n argocd -l app.kubernetes.io/name=identity-docs -o wide || echo "No Applications found" | |
| # Check if target namespaces exist | |
| echo "" | |
| echo "🏷️ Target Namespaces:" | |
| kubectl get namespace docs || echo "docs namespace not found (will be created by ArgoCD)" | |
| kubectl get namespace docs-staging || echo "docs-staging namespace not found (will be created by ArgoCD)" | |
| # Show specific application for the deployed environment | |
| if [[ -n "$DEPLOYMENT_ENVIRONMENT" ]]; then | |
| echo "" | |
| echo "🎯 Target Application (identity-docs-$DEPLOYMENT_ENVIRONMENT):" | |
| kubectl get application -n argocd identity-docs-$DEPLOYMENT_ENVIRONMENT -o yaml 2>/dev/null | grep -A 5 -B 5 "health\|sync" || echo "Application not found or not ready yet" | |
| fi | |
| echo "" | |
| echo "🎉 ArgoCD ApplicationSet deployment completed!" | |
| echo "📍 Check ArgoCD UI for application status and sync progress" | |
| echo "" | |
| echo "🔗 Deployment Summary:" | |
| echo " - Environment: $DEPLOYMENT_ENVIRONMENT" | |
| echo " - Version: $DEPLOYMENT_VERSION" | |
| echo " - Chart Version: $CHART_VERSION" | |
| echo "" | |
| echo "📊 Expected URLs:" | |
| if [[ "$DEPLOYMENT_ENVIRONMENT" == "production" ]]; then | |
| echo " - Production: https://docs.itlusions.com/identity/" | |
| else | |
| echo " - Staging: https://docs-staging.itlusions.com/identity/" | |
| fi | |
| echo "" | |
| echo "⏰ Note: Applications may take 1-3 minutes to fully sync and become available" | |
| # Deploy only for version tags | |
| deploy: | |
| needs: [build, helm-package, security-scan, argocd-deploy] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| environment: production | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Helm | |
| uses: azure/setup-helm@v3 | |
| with: | |
| version: '3.12.0' | |
| - name: Generate production manifests | |
| run: | | |
| helm template identity-docs charts/identity-docs \ | |
| --set image.tag=${{ github.ref_name }} \ | |
| --set ingress.host=docs.itlusions.com \ | |
| --validate=false > manifests-production.yaml | |
| - name: Deploy to production | |
| run: | | |
| echo "Deploying to production environment" | |
| echo "Version: ${{ github.ref_name }}" | |
| echo "Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}" | |
| # Add your production deployment logic here | |
| # kubectl apply -f manifests-production.yaml or ArgoCD sync | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.11' | |
| - name: Build documentation site | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| mkdocs build | |
| - name: Create release artifacts | |
| run: | | |
| # Create release package directory | |
| mkdir -p release-artifacts | |
| # Create documentation archive | |
| tar -czf release-artifacts/identity-docs-site-${{ github.ref_name }}.tar.gz -C site . | |
| # Package Helm chart for release (local copy) | |
| CHART_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') | |
| helm package charts/identity-docs \ | |
| --version "$CHART_VERSION" \ | |
| --app-version "${{ github.ref_name }}" \ | |
| --destination release-artifacts/ | |
| # Download the installation instructions from the helm-package job | |
| echo "📋 Creating Helm installation guide..." | |
| cat > release-artifacts/HELM_INSTALLATION.md << EOF | |
| # Identity Platform Helm Chart Installation Guide | |
| ## Version: ${{ github.ref_name }} | |
| ### Option 1: Install from GitHub Container Registry (Recommended) | |
| \`\`\`bash | |
| # Login to GitHub Container Registry | |
| helm registry login ghcr.io -u ${{ github.actor }} | |
| # Install the chart | |
| helm install identity-docs oci://ghcr.io/itlusions/helm/identity-docs --version $CHART_VERSION | |
| \`\`\` | |
| ### Option 2: Install from Release Assets | |
| \`\`\`bash | |
| # Download the chart from this release | |
| wget https://github.com/ITlusions/ITL.identity.platform/releases/download/${{ github.ref_name }}/identity-docs-$CHART_VERSION.tgz | |
| # Install the chart | |
| helm install identity-docs ./identity-docs-$CHART_VERSION.tgz | |
| \`\`\` | |
| ### Custom Values Example | |
| \`\`\`yaml | |
| # values.yaml | |
| image: | |
| tag: "${{ github.ref_name }}" | |
| ingress: | |
| enabled: true | |
| hosts: | |
| - host: docs.yourdomain.com | |
| paths: | |
| - path: /identity | |
| pathType: Prefix | |
| resources: | |
| limits: | |
| cpu: 200m | |
| memory: 256Mi | |
| requests: | |
| cpu: 100m | |
| memory: 128Mi | |
| \`\`\` | |
| \`\`\`bash | |
| helm install identity-docs oci://ghcr.io/itlusions/helm/identity-docs \\ | |
| --version $CHART_VERSION \\ | |
| --values values.yaml | |
| \`\`\` | |
| ### Chart Information | |
| - **Chart Version**: $CHART_VERSION | |
| - **App Version**: ${{ github.ref_name }} | |
| - **OCI Registry**: oci://ghcr.io/itlusions/helm/identity-docs | |
| - **Documentation**: https://docs.itlusions.com/identity/ | |
| - **Repository**: https://github.com/ITlusions/ITL.identity.platform | |
| EOF | |
| echo "📦 Release artifacts created:" | |
| ls -la release-artifacts/ | |
| - name: Deploy to Central Hub | |
| run: | | |
| echo "Deploying Identity Platform docs to central hub" | |
| echo "Version: ${{ github.ref_name }}" | |
| echo "Subpath: /identity" | |
| echo "Central Hub URL: https://docs.itlusions.com/identity/" | |
| # Generate manifests for central hub deployment | |
| CHART_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') | |
| helm template identity-docs charts/identity-docs \ | |
| --set image.tag=${{ github.ref_name }} \ | |
| --set ingress.hosts[0].host=docs.itlusions.com \ | |
| --set hub.enabled=true \ | |
| --set hub.subpath=identity \ | |
| --validate=false > manifests-central-hub.yaml | |
| echo "Generated manifests for central hub integration" | |
| # Here you would typically: | |
| # 1. Apply to your Kubernetes cluster | |
| # 2. Update ArgoCD application | |
| # 3. Or sync with your GitOps repository | |
| # Example ArgoCD sync (uncomment when ready): | |
| # argocd app sync identity-docs --revision ${{ github.ref_name }} | |
| - name: Update Central Hub Registry | |
| run: | | |
| echo "Registering Identity Platform with central hub" | |
| # Create service registration payload | |
| cat > service-registration.json << EOF | |
| { | |
| "name": "Identity Platform", | |
| "path": "/identity", | |
| "description": "Authentication and authorization platform", | |
| "version": "${{ github.ref_name }}", | |
| "status": "production", | |
| "health_endpoint": "/identity/health", | |
| "documentation_url": "https://docs.itlusions.com/identity/", | |
| "repository": "${{ github.repository }}", | |
| "last_updated": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", | |
| "helm_chart": { | |
| "registry": "oci://ghcr.io/itlusions/helm/identity-docs", | |
| "version": "$(echo "${{ github.ref_name }}" | sed 's/^v//')" | |
| } | |
| } | |
| EOF | |
| echo "Service registration payload created" | |
| cat service-registration.json | |
| # This would typically POST to your central hub's API | |
| # curl -X POST https://docs.itlusions.com/api/services \ | |
| # -H "Content-Type: application/json" \ | |
| # -H "Authorization: Bearer ${{ secrets.HUB_API_TOKEN }}" \ | |
| # -d @service-registration.json | |
| - name: Upload Artifacts to Existing Release | |
| run: | | |
| # Upload artifacts to the existing release | |
| echo "Uploading artifacts to existing release ${{ github.ref_name }}..." | |
| # Check if release exists | |
| if gh release view "${{ github.ref_name }}" >/dev/null 2>&1; then | |
| echo "✅ Release ${{ github.ref_name }} exists" | |
| # Upload all artifacts to the existing release | |
| for file in release-artifacts/*; do | |
| echo "📦 Uploading: $(basename "$file")" | |
| gh release upload "${{ github.ref_name }}" "$file" --clobber | |
| done | |
| echo "✅ All artifacts uploaded successfully" | |
| else | |
| echo "❌ Release ${{ github.ref_name }} does not exist" | |
| echo "Please create the release manually first, then re-run the workflow" | |
| exit 1 | |
| fi | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| notify-teams: | |
| needs: [deploy, argocd-deploy] | |
| runs-on: ubuntu-latest | |
| if: always() && needs.deploy.result != 'cancelled' | |
| steps: | |
| - name: Notify Microsoft Teams | |
| shell: bash | |
| run: | | |
| # Check if Teams webhook is configured | |
| if [ -z "${{ secrets.TEAMS_WEBHOOK }}" ]; then | |
| echo "🔕 Teams webhook not configured, skipping notification" | |
| exit 0 | |
| fi | |
| echo "📧 Preparing Teams notification..." | |
| echo "Status: ${{ needs.deploy.result }}" | |
| echo "Actor: ${{ github.actor }}" | |
| echo "Ref: ${{ github.ref_name }}" | |
| echo "Event: ${{ github.event_name }}" | |
| # Determine trigger source | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| TRIGGER="Manual (by ${{ github.actor }})" | |
| if [ "${{ inputs.environment }}" != "" ]; then | |
| TRIGGER="$TRIGGER → ${{ inputs.environment }}" | |
| fi | |
| else | |
| TRIGGER="Automatic (${{ github.event_name }})" | |
| fi | |
| echo "Trigger: $TRIGGER" | |
| # Determine status and color | |
| STATUS="${{ needs.deploy.result }}" | |
| case "$STATUS" in | |
| "success") COLOR="Good"; EMOJI="✅" ;; | |
| "failure") COLOR="Attention"; EMOJI="❌" ;; | |
| "cancelled") COLOR="Warning"; EMOJI="⚠️" ;; | |
| *) COLOR="Warning"; EMOJI="⚠️" ;; | |
| esac | |
| # Truncate commit SHA for display | |
| SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-8) | |
| # Truncate webhook URL for logging (show only first 50 chars) | |
| WEBHOOK_PREVIEW=$(echo "${{ secrets.TEAMS_WEBHOOK }}" | cut -c1-50) | |
| echo "🔗 Webhook URL: ${WEBHOOK_PREVIEW}..." | |
| # Validate webhook URL format | |
| if [[ "${{ secrets.TEAMS_WEBHOOK }}" == *"outlook.office.com/webhook"* ]]; then | |
| echo "✅ Direct Teams webhook detected" | |
| MESSAGE_TYPE="teams" | |
| elif [[ "${{ secrets.TEAMS_WEBHOOK }}" == *"powerplatform.com"* ]] && [[ "${{ secrets.TEAMS_WEBHOOK }}" == *"powerautomate"* ]]; then | |
| echo "✅ Power Automate workflow webhook detected" | |
| MESSAGE_TYPE="powerautomate" | |
| else | |
| echo "⚠️ Warning: Webhook URL format may be incorrect" | |
| echo "Expected: Teams webhook or Power Automate workflow URL" | |
| MESSAGE_TYPE="unknown" | |
| fi | |
| echo "🎨 Message color: $COLOR ($EMOJI)" | |
| # Create appropriate message payload based on webhook type | |
| if [ "$MESSAGE_TYPE" = "powerautomate" ]; then | |
| echo "📝 Creating Power Automate payload..." | |
| # Power Automate expects simpler JSON structure | |
| CHART_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') | |
| cat > teams_payload.json << EOF | |
| { | |
| "title": "ITL Identity Platform Deployment", | |
| "status": "$STATUS", | |
| "emoji": "$EMOJI", | |
| "color": "$COLOR", | |
| "version": "${{ github.ref_name }}", | |
| "commit": "$SHORT_SHA", | |
| "actor": "${{ github.actor }}", | |
| "trigger": "$TRIGGER", | |
| "workflow_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", | |
| "repository": "${{ github.repository }}", | |
| "event": "${{ github.event_name }}", | |
| "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", | |
| "helm_chart": { | |
| "version": "$CHART_VERSION", | |
| "registry": "oci://ghcr.io/itlusions/helm/identity-docs" | |
| }, | |
| "argocd_deployment": "${{ needs.argocd-deploy.result }}", | |
| "documentation_url": "https://docs.itlusions.com/identity/" | |
| } | |
| EOF | |
| else | |
| echo "📝 Creating Teams MessageCard payload..." | |
| # Direct Teams webhook expects MessageCard format | |
| CHART_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') | |
| cat > teams_payload.json << EOF | |
| { | |
| "@type": "MessageCard", | |
| "@context": "http://schema.org/extensions", | |
| "summary": "ITL Identity Platform Deployment", | |
| "themeColor": "$COLOR", | |
| "sections": [ | |
| { | |
| "activityTitle": "ITL Identity Platform", | |
| "activitySubtitle": "Build and Deploy Documentation", | |
| "activityImage": "https://github.com/ITlusions.png", | |
| "facts": [ | |
| { | |
| "name": "Status:", | |
| "value": "$STATUS $EMOJI" | |
| }, | |
| { | |
| "name": "Version:", | |
| "value": "${{ github.ref_name }}" | |
| }, | |
| { | |
| "name": "Commit:", | |
| "value": "$SHORT_SHA" | |
| }, | |
| { | |
| "name": "Trigger:", | |
| "value": "$TRIGGER" | |
| }, | |
| { | |
| "name": "Actor:", | |
| "value": "${{ github.actor }}" | |
| }, | |
| { | |
| "name": "Helm Chart:", | |
| "value": "v$CHART_VERSION available in GHCR" | |
| }, | |
| { | |
| "name": "ArgoCD Deployment:", | |
| "value": "${{ needs.argocd-deploy.result == 'success' && '✅ ApplicationSet Updated' || needs.argocd-deploy.result == 'failure' && '❌ ApplicationSet Failed' || needs.argocd-deploy.result == 'skipped' && '⏭️ Skipped' || '⏳ In Progress' }}" | |
| } | |
| ], | |
| "markdown": true | |
| } | |
| ], | |
| "potentialAction": [ | |
| { | |
| "@type": "OpenUri", | |
| "name": "View Workflow", | |
| "targets": [ | |
| { | |
| "os": "default", | |
| "uri": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| } | |
| ] | |
| }, | |
| { | |
| "@type": "OpenUri", | |
| "name": "View Documentation", | |
| "targets": [ | |
| { | |
| "os": "default", | |
| "uri": "https://docs.itlusions.com/identity/" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| EOF | |
| fi | |
| echo "📝 Message payload created for $MESSAGE_TYPE" | |
| echo "📄 Payload preview:" | |
| jq '.' teams_payload.json || cat teams_payload.json | |
| # Send to Teams | |
| echo "📤 Sending notification to Teams..." | |
| RESPONSE=$(curl -s -w "\n%{http_code}" -H "Content-Type: application/json" \ | |
| -d @teams_payload.json \ | |
| "${{ secrets.TEAMS_WEBHOOK }}") | |
| HTTP_CODE=$(echo "$RESPONSE" | tail -n1) | |
| RESPONSE_BODY=$(echo "$RESPONSE" | head -n -1) | |
| echo "📊 HTTP Response Code: $HTTP_CODE" | |
| if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "202" ]; then | |
| echo "✅ Teams notification sent successfully!" | |
| if [ "$HTTP_CODE" = "202" ]; then | |
| echo "ℹ️ Message accepted for async processing" | |
| fi | |
| echo "" | |
| echo "🔍 Troubleshooting if no message appears in Teams:" | |
| echo "1. Check if the webhook connector is still active in Teams" | |
| echo "2. Verify the Teams channel where the connector was configured" | |
| echo "3. Check if the connector has been removed or disabled" | |
| echo "4. Try sending a test message to the webhook URL manually" | |
| else | |
| echo "❌ Teams notification failed!" | |
| echo "Response: $RESPONSE_BODY" | |
| # Don't exit with error for notification failures | |
| fi | |
| env: | |
| TEAMS_WEBHOOK: ${{ secrets.TEAMS_WEBHOOK }} |