Skip to content

feat: Add comprehensive emergency deployment procedures documentation #52

feat: Add comprehensive emergency deployment procedures documentation

feat: Add comprehensive emergency deployment procedures documentation #52

Workflow file for this run

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 }}