sync
This commit is contained in:
19
.gitea/actions/extract-namespace-from-repo-name/action.yaml
Normal file
19
.gitea/actions/extract-namespace-from-repo-name/action.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: "Extract namespace from repo name"
|
||||||
|
description: "Extracts the namespace name from the repo name, based on the convention of k_<namespace-name>"
|
||||||
|
inputs:
|
||||||
|
repo:
|
||||||
|
description: 'The repo name, get it from "github.repository"'
|
||||||
|
required: true
|
||||||
|
outputs:
|
||||||
|
namespace:
|
||||||
|
description: "The namespace name"
|
||||||
|
value: ${{ steps.extract.outputs.suffix }}
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- id: extract
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
full_repo="${{ inputs.repo }}"
|
||||||
|
suffix="${full_repo##*k_}"
|
||||||
|
echo "suffix=$suffix" >> $GITHUB_OUTPUT
|
||||||
197
.gitea/workflows/cd.yaml
Normal file
197
.gitea/workflows/cd.yaml
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
# Optional: Override the default k8s directory path
|
||||||
|
k8s_dir:
|
||||||
|
description: "Path to Kubernetes manifests directory"
|
||||||
|
required: false
|
||||||
|
default: "k8s/"
|
||||||
|
type: string
|
||||||
|
# Optional: Override the default helmfile path
|
||||||
|
helmfile_path:
|
||||||
|
description: "Path to helmfile.yaml"
|
||||||
|
required: false
|
||||||
|
default: "helmfile.yaml"
|
||||||
|
type: string
|
||||||
|
# Optional: Skip Helm deployment even if helmfile exists
|
||||||
|
skip_helm_deployment:
|
||||||
|
description: "Skip Helm deployment even if helmfile.yaml exists"
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
# Optional: Custom secrets to create (JSON array of secret objects)
|
||||||
|
custom_secrets:
|
||||||
|
description: "JSON array of secrets to create. Each secret should have: name, type, data"
|
||||||
|
required: false
|
||||||
|
default: "[]"
|
||||||
|
type: string
|
||||||
|
# Optional: Branch to deploy from
|
||||||
|
deploy_branch:
|
||||||
|
description: "Branch to deploy from"
|
||||||
|
required: false
|
||||||
|
default: "main"
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
detect-service-type:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
has_helmfile: ${{ steps.check-helmfile.outputs.exists }}
|
||||||
|
has_k8s: ${{ steps.check-k8s.outputs.exists }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Check if helmfile.yaml exists
|
||||||
|
id: check-helmfile
|
||||||
|
run: |
|
||||||
|
if [ -f "${{ inputs.helmfile_path }}" ]; then
|
||||||
|
echo "exists=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "Found helmfile.yaml at ${{ inputs.helmfile_path }}"
|
||||||
|
else
|
||||||
|
echo "exists=false" >> $GITHUB_OUTPUT
|
||||||
|
echo "No helmfile.yaml found at ${{ inputs.helmfile_path }}"
|
||||||
|
fi
|
||||||
|
- name: Check if k8s directory exists
|
||||||
|
id: check-k8s
|
||||||
|
run: |
|
||||||
|
if [ -d "${{ inputs.k8s_dir }}" ]; then
|
||||||
|
echo "exists=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "Found k8s directory at ${{ inputs.k8s_dir }}"
|
||||||
|
else
|
||||||
|
echo "exists=false" >> $GITHUB_OUTPUT
|
||||||
|
echo "No k8s directory found at ${{ inputs.k8s_dir }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
deploy-k8s:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: detect-service-type
|
||||||
|
if: needs.detect-service-type.outputs.has_k8s == 'true'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.deploy_branch }}
|
||||||
|
- uses: ./.gitea/actions/extract-namespace-from-repo-name
|
||||||
|
id: namespace
|
||||||
|
with:
|
||||||
|
repo: ${{ github.repository }}
|
||||||
|
- uses: azure/setup-kubectl@v4
|
||||||
|
- uses: azure/k8s-set-context@v4
|
||||||
|
with:
|
||||||
|
method: kubeconfig
|
||||||
|
kubeconfig: ${{ secrets.KUBECONFIG }}
|
||||||
|
- name: Create custom secrets
|
||||||
|
id: create-secrets
|
||||||
|
run: |
|
||||||
|
# Parse custom secrets from input
|
||||||
|
SECRETS='${{ inputs.custom_secrets }}'
|
||||||
|
if [ "$SECRETS" != "[]" ]; then
|
||||||
|
echo "Creating custom secrets..."
|
||||||
|
echo "$SECRETS" | jq -c '.[]' | while read -r secret; do
|
||||||
|
SECRET_NAME=$(echo "$secret" | jq -r '.name')
|
||||||
|
SECRET_TYPE=$(echo "$secret" | jq -r '.type // "generic"')
|
||||||
|
SECRET_DATA=$(echo "$secret" | jq -r '.data')
|
||||||
|
|
||||||
|
echo "Creating secret: $SECRET_NAME (type: $SECRET_TYPE)"
|
||||||
|
|
||||||
|
# Create the secret using kubectl
|
||||||
|
echo "$SECRET_DATA" | kubectl create secret $SECRET_TYPE $SECRET_NAME \
|
||||||
|
--from-literal=secret.json="$SECRET_DATA" \
|
||||||
|
--namespace=${{ steps.namespace.outputs.namespace }} \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "No custom secrets to create"
|
||||||
|
fi
|
||||||
|
- name: Deploy Kubernetes manifests
|
||||||
|
uses: azure/k8s-deploy@v5.0.4
|
||||||
|
with:
|
||||||
|
action: deploy
|
||||||
|
manifests: "${{ inputs.k8s_dir }}"
|
||||||
|
strategy: basic
|
||||||
|
namespace: ${{ steps.namespace.outputs.namespace }}
|
||||||
|
|
||||||
|
deploy-helm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: detect-service-type
|
||||||
|
if: |
|
||||||
|
needs.detect-service-type.outputs.has_helmfile == 'true' &&
|
||||||
|
needs.detect-service-type.outputs.has_k8s == 'true' &&
|
||||||
|
inputs.skip_helm_deployment != 'true'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.deploy_branch }}
|
||||||
|
- uses: ./.gitea/actions/extract-namespace-from-repo-name
|
||||||
|
id: namespace
|
||||||
|
with:
|
||||||
|
repo: ${{ github.repository }}
|
||||||
|
- uses: azure/setup-kubectl@v4
|
||||||
|
- uses: azure/setup-helm@v4
|
||||||
|
- uses: azure/k8s-set-context@v4
|
||||||
|
with:
|
||||||
|
method: kubeconfig
|
||||||
|
kubeconfig: ${{ secrets.KUBECONFIG }}
|
||||||
|
- name: Create custom secrets
|
||||||
|
id: create-secrets
|
||||||
|
run: |
|
||||||
|
# Parse custom secrets from input
|
||||||
|
SECRETS='${{ inputs.custom_secrets }}'
|
||||||
|
if [ "$SECRETS" != "[]" ]; then
|
||||||
|
echo "Creating custom secrets..."
|
||||||
|
echo "$SECRETS" | jq -c '.[]' | while read -r secret; do
|
||||||
|
SECRET_NAME=$(echo "$secret" | jq -r '.name')
|
||||||
|
SECRET_TYPE=$(echo "$secret" | jq -r '.type // "generic"')
|
||||||
|
SECRET_DATA=$(echo "$secret" | jq -r '.data')
|
||||||
|
|
||||||
|
echo "Creating secret: $SECRET_NAME (type: $SECRET_TYPE)"
|
||||||
|
|
||||||
|
# Create the secret using kubectl
|
||||||
|
echo "$SECRET_DATA" | kubectl create secret $SECRET_TYPE $SECRET_NAME \
|
||||||
|
--from-literal=secret.json="$SECRET_DATA" \
|
||||||
|
--namespace=${{ steps.namespace.outputs.namespace }} \
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "No custom secrets to create"
|
||||||
|
fi
|
||||||
|
- name: Deploy Helm
|
||||||
|
uses: helmfile/helmfile-action@v2
|
||||||
|
with:
|
||||||
|
helmfile-args: apply
|
||||||
|
|
||||||
|
# Summary job that always runs to show what was deployed
|
||||||
|
deployment-summary:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [detect-service-type, deploy-k8s, deploy-helm]
|
||||||
|
if: always()
|
||||||
|
steps:
|
||||||
|
- name: Deployment Summary
|
||||||
|
run: |
|
||||||
|
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
if [ "${{ needs.detect-service-type.outputs.has_k8s }}" == "true" ]; then
|
||||||
|
echo "✅ **Kubernetes deployment**: Completed" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "❌ **Kubernetes deployment**: Skipped (no k8s/ directory found)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ needs.detect-service-type.outputs.has_helmfile }}" == "true" ] && [ "${{ inputs.skip_helm_deployment }}" != "true" ]; then
|
||||||
|
echo "✅ **Helm deployment**: Completed" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [ "${{ needs.detect-service-type.outputs.has_helmfile }}" == "true" ] && [ "${{ inputs.skip_helm_deployment }}" == "true" ]; then
|
||||||
|
echo "⏭️ **Helm deployment**: Skipped (manually disabled)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "⏭️ **Helm deployment**: Skipped (no helmfile.yaml found)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Service Type**: ${{ needs.detect-service-type.outputs.has_helmfile == 'true' && 'Helm + Kubernetes' || 'Kubernetes Only' }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Show custom secrets info
|
||||||
|
SECRETS='${{ inputs.custom_secrets }}'
|
||||||
|
if [ "$SECRETS" != "[]" ]; then
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Custom Secrets Created**: $(echo "$SECRETS" | jq length)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "$SECRETS" | jq -r '.[] | "- " + .name + " (" + (.type // "generic") + ")"' >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
277
CD-README.md
Normal file
277
CD-README.md
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
# 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
|
||||||
117
README.md
Normal file
117
README.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# Reusable CI Workflow for Kubernetes Services
|
||||||
|
|
||||||
|
This directory contains a reusable CI workflow that automatically detects and validates your Kubernetes services, whether they use Helm + Kubernetes or just Kubernetes manifests.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Automatic Detection**: Automatically detects if your service uses Helm (helmfile.yaml) or just Kubernetes manifests
|
||||||
|
- **Conditional Validation**: Only runs Helm validation when helmfile.yaml exists
|
||||||
|
- **Flexible Paths**: Configurable paths for k8s directory and helmfile
|
||||||
|
- **Comprehensive Validation**: Validates both Kubernetes manifests and Helm charts
|
||||||
|
- **CI Summary**: Provides a clear summary of what was validated
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Usage (Recommended)
|
||||||
|
|
||||||
|
Simply call the workflow without any parameters - it will automatically detect your service type:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
uses: ./.gitea/workflows/ci.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Usage with Custom Paths
|
||||||
|
|
||||||
|
If your service uses non-standard directory names:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
uses: ./.gitea/workflows/ci.yaml
|
||||||
|
with:
|
||||||
|
k8s_dir: 'kubernetes/'
|
||||||
|
helmfile_path: 'helm/helmfile.yaml'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Force Skip Helm Validation
|
||||||
|
|
||||||
|
If you want to skip Helm validation even when helmfile.yaml exists:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
uses: ./.gitea/workflows/ci.yaml
|
||||||
|
with:
|
||||||
|
skip_helm_validation: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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_validation` | Skip Helm validation even if helmfile exists | `false` | No |
|
||||||
|
|
||||||
|
## 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 Validated
|
||||||
|
|
||||||
|
### Always (if k8s/ directory exists):
|
||||||
|
- Kubernetes manifest validation using `kubectl --dry-run`
|
||||||
|
- Namespace extraction from repository name
|
||||||
|
- Basic Kubernetes syntax and schema validation
|
||||||
|
|
||||||
|
### Conditionally (if helmfile.yaml exists and Helm validation not skipped):
|
||||||
|
- Helm chart validation using `helmfile diff`
|
||||||
|
- Kubernetes manifests in Helm context
|
||||||
|
- Helm-specific configurations and values
|
||||||
|
|
||||||
|
## Example Workflows
|
||||||
|
|
||||||
|
See `example-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
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Helm validation skipped unexpectedly
|
||||||
|
- Check if `helmfile.yaml` exists in the expected location
|
||||||
|
- Verify the `skip_helm_validation` parameter is not set to `true`
|
||||||
|
- Ensure the file path is correct if using custom paths
|
||||||
|
|
||||||
|
### Kubernetes validation skipped
|
||||||
|
- Verify the `k8s/` directory (or custom path) exists
|
||||||
|
- Check the directory contains valid Kubernetes manifests
|
||||||
|
|
||||||
|
### Permission issues
|
||||||
|
- Ensure the `KUBECONFIG` secret is properly configured
|
||||||
|
- Verify the workflow has access to your Kubernetes cluster
|
||||||
Reference in New Issue
Block a user