7.9 KiB
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:
jobs:
deploy:
uses: ./.gitea/workflows/cd.yaml
With Custom Secrets
For services that need custom secrets (like your original example):
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:
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:
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:
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:
{
"name": "secret-name",
"type": "secret-type",
"data": "{\"key1\": \"value1\", \"key2\": \"value2\"}"
}
Secret Object Properties
name(required): The name of the Kubernetes secrettype(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 credentialstls: For TLS certificatesssh-auth: For SSH authenticationbasic-auth: For basic authentication
Example Secret Configurations
OIDC Configuration
{
"name": "oidc",
"type": "generic",
"data": "{\"clientId\": \"${{ secrets.OIDC_CLIENT_ID }}\", \"clientSecret\": \"${{ secrets.OIDC_CLIENT_SECRET }}\"}"
}
Database Credentials
{
"name": "database-credentials",
"type": "generic",
"data": "{\"username\": \"${{ secrets.DB_USERNAME }}\", \"password\": \"${{ secrets.DB_PASSWORD }}\"}"
}
Docker Registry
{
"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:
oidcsecret with OIDC_CLIENT_ID and OIDC_CLIENT_SECRETroot-usersecret with MINIO_ROOT_USER and MINIO_ROOT_PASSWORD
To replicate this in the new workflow:
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-nameactionKUBECONFIGsecret configured in your repository- Access to your Kubernetes cluster
jqfor JSON parsing (included in ubuntu-latest runner)
Troubleshooting
Helm deployment skipped unexpectedly
- Check if
helmfile.yamlexists in the expected location - Verify the
skip_helm_deploymentparameter is not set totrue - 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
datafield contains valid JSON string
Permission issues
- Ensure the
KUBECONFIGsecret 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
datafield 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