# Reusable CD Workflow for Kubernetes Services This directory contains a reusable CD (Continuous Deployment) workflow that automatically detects and deploys your Kubernetes services, whether they use Helm + Kubernetes or just Kubernetes manifests, with flexible secret management. ## Features - **Automatic Detection**: Automatically detects if your service uses Helm (helmfile.yaml) or just Kubernetes manifests - **Conditional Deployment**: Only runs Helm deployment when helmfile.yaml exists - **Flexible Paths**: Configurable paths for k8s directory and helmfile - **Smart Secret Management**: Flexible JSON-based secret creation system - **Branch Flexibility**: Deploy from any branch (defaults to main) - **Comprehensive Summary**: Provides a clear summary of what was deployed ## Usage ### Basic Usage (Recommended) Simply call the workflow without any parameters - it will automatically detect your service type: ```yaml jobs: deploy: uses: ./.gitea/workflows/cd.yaml ``` ### With Custom Secrets For services that need custom secrets (like your original example): ```yaml jobs: deploy: uses: ./.gitea/workflows/cd.yaml with: custom_secrets: | [ { "name": "oidc", "type": "generic", "data": "{\"clientId\": \"${{ secrets.OIDC_CLIENT_ID }}\", \"clientSecret\": \"${{ secrets.OIDC_CLIENT_SECRET }}\"}" }, { "name": "root-user", "type": "generic", "data": "{\"rootUser\": \"${{ secrets.MINIO_ROOT_USER }}\", \"rootPassword\": \"${{ secrets.MINIO_ROOT_PASSWORD }}\"}" } ] ``` ### Advanced Usage with Custom Paths If your service uses non-standard directory names: ```yaml jobs: deploy: uses: ./.gitea/workflows/cd.yaml with: k8s_dir: 'kubernetes/' helmfile_path: 'helm/helmfile.yaml' custom_secrets: | [ { "name": "database-credentials", "type": "generic", "data": "{\"username\": \"${{ secrets.DB_USERNAME }}\", \"password\": \"${{ secrets.DB_PASSWORD }}\"}" } ] ``` ### Deploy from Different Branch Deploy from a staging or feature branch: ```yaml jobs: deploy-staging: uses: ./.gitea/workflows/cd.yaml with: deploy_branch: 'staging' custom_secrets: | [ { "name": "staging-config", "type": "generic", "data": "{\"environment\": \"staging\", \"debug\": \"true\"}" } ] ``` ### Force Skip Helm Deployment If you want to skip Helm deployment even when helmfile.yaml exists: ```yaml jobs: deploy: uses: ./.gitea/workflows/cd.yaml with: skip_helm_deployment: true custom_secrets: | [ { "name": "app-config", "type": "generic", "data": "{\"apiUrl\": \"${{ secrets.API_URL }}\", \"apiKey\": \"${{ secrets.API_KEY }}\"}" } ] ``` ## Input Parameters | Parameter | Description | Default | Required | |-----------|-------------|---------|----------| | `k8s_dir` | Path to Kubernetes manifests directory | `k8s/` | No | | `helmfile_path` | Path to helmfile.yaml | `helmfile.yaml` | No | | `skip_helm_deployment` | Skip Helm deployment even if helmfile exists | `false` | No | | `custom_secrets` | JSON array of secrets to create | `[]` | No | | `deploy_branch` | Branch to deploy from | `main` | No | ## Custom Secrets Format The `custom_secrets` parameter accepts a JSON array where each secret object has: ```json { "name": "secret-name", "type": "secret-type", "data": "{\"key1\": \"value1\", \"key2\": \"value2\"}" } ``` ### Secret Object Properties - **`name`** (required): The name of the Kubernetes secret - **`type`** (optional): The type of Kubernetes secret (defaults to "generic") - **`data`** (required): JSON string containing the secret data ### Supported Secret Types - `generic`: Generic secret (most common) - `docker-registry`: For Docker registry credentials - `tls`: For TLS certificates - `ssh-auth`: For SSH authentication - `basic-auth`: For basic authentication ### Example Secret Configurations #### OIDC Configuration ```json { "name": "oidc", "type": "generic", "data": "{\"clientId\": \"${{ secrets.OIDC_CLIENT_ID }}\", \"clientSecret\": \"${{ secrets.OIDC_CLIENT_SECRET }}\"}" } ``` #### Database Credentials ```json { "name": "database-credentials", "type": "generic", "data": "{\"username\": \"${{ secrets.DB_USERNAME }}\", \"password\": \"${{ secrets.DB_PASSWORD }}\"}" } ``` #### Docker Registry ```json { "name": "docker-registry", "type": "docker-registry", "data": "{\"username\": \"${{ secrets.DOCKER_USERNAME }}\", \"password\": \"${{ secrets.DOCKER_PASSWORD }}\"}" } ``` ## Directory Structure Requirements ### For Kubernetes-only services: ``` your-service/ ├── k8s/ │ ├── deployment.yaml │ ├── service.yaml │ └── ... └── .gitea/workflows/your-workflow.yaml ``` ### For Helm + Kubernetes services: ``` your-service/ ├── k8s/ │ ├── deployment.yaml │ ├── service.yaml │ └── ... ├── helmfile.yaml └── .gitea/workflows/your-workflow.yaml ``` ## What Gets Deployed ### Always (if k8s/ directory exists): - Custom secrets creation (if specified) - Kubernetes manifest deployment using `kubectl apply` - Namespace extraction from repository name ### Conditionally (if helmfile.yaml exists and Helm deployment not skipped): - Custom secrets creation (if specified) - Helm chart deployment using `helmfile apply` - Kubernetes manifests in Helm context ## Migration from Your Original Workflow Your original workflow had these hardcoded secrets: - `oidc` secret with OIDC_CLIENT_ID and OIDC_CLIENT_SECRET - `root-user` secret with MINIO_ROOT_USER and MINIO_ROOT_PASSWORD To replicate this in the new workflow: ```yaml jobs: deploy: uses: ./.gitea/workflows/cd.yaml with: custom_secrets: | [ { "name": "oidc", "type": "generic", "data": "{\"clientId\": \"${{ secrets.OIDC_CLIENT_ID }}\", \"clientSecret\": \"${{ secrets.OIDC_CLIENT_SECRET }}\"}" }, { "name": "root-user", "type": "generic", "data": "{\"rootUser\": \"${{ secrets.MINIO_ROOT_USER }}\", \"rootPassword\": \"${{ secrets.MINIO_ROOT_PASSWORD }}\"}" } ] ``` ## Example Workflows See `example-cd-usage.yaml` for complete examples of how to use this workflow in different scenarios. ## Dependencies This workflow requires: - `./.gitea/actions/extract-namespace-from-repo-name` action - `KUBECONFIG` secret configured in your repository - Access to your Kubernetes cluster - `jq` for JSON parsing (included in ubuntu-latest runner) ## Troubleshooting ### Helm deployment skipped unexpectedly - Check if `helmfile.yaml` exists in the expected location - Verify the `skip_helm_deployment` parameter is not set to `true` - Ensure the file path is correct if using custom paths ### Kubernetes deployment skipped - Verify the `k8s/` directory (or custom path) exists - Check the directory contains valid Kubernetes manifests ### Secret creation failures - Verify the JSON format is valid - Check that secret names are valid Kubernetes resource names - Ensure the `data` field contains valid JSON string ### Permission issues - Ensure the `KUBECONFIG` secret is properly configured - Verify the workflow has access to your Kubernetes cluster - Check that the namespace exists and is accessible ## Security Considerations - **Secret Data**: The `data` field in custom_secrets should contain the actual secret values, not references to GitHub secrets - **Repository Secrets**: Store sensitive values in GitHub repository secrets and reference them in the workflow - **Access Control**: Ensure only authorized users can trigger deployments - **Audit Trail**: All deployments are logged and visible in the GitHub Actions history