A GitHub Action that compares local Kubernetes manifests against live cluster state using kube-diff, and optionally posts the diff as a PR comment.
- Compare plain YAML, Helm charts, or Kustomize overlays against your cluster
- Auto-post diff results as PR comments (updates existing comment on re-run)
- Multiple output formats:
color,plain,json,markdown,table - Ignore specific fields in diff with
ignore-field - Configurable context lines, exit code behavior, and diff strategy
- Compare against live state or
last-applied-configurationannotation - Filter by namespace, kind, or label selector
- Detects drift: changed, new, deleted, and unchanged resources
- name: Check for drift
uses: somaz94/kube-diff-action@v1
with:
source: file
path: ./manifests/
namespace: production- uses: somaz94/kube-diff-action@v1
with:
source: file
path: ./manifests/
namespace: production
kind: Deployment,Service- uses: somaz94/kube-diff-action@v1
with:
source: helm
path: ./my-chart/
values: values-prod.yaml
release: my-release
namespace: production- uses: somaz94/kube-diff-action@v1
with:
source: kustomize
path: ./overlays/production/The comment follows output: markdown, plain and color fold each changed resource's diff into a collapsible section, while json and table are posted as a code block. A report too large for a PR comment is replaced by its summary line and a link to the workflow run.
name: Drift Check
on:
pull_request:
paths:
- 'manifests/**'
jobs:
drift-check:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Setup kubeconfig
# Configure your cluster access here
run: echo "${{ secrets.KUBECONFIG }}" > /tmp/kubeconfig
- name: Check drift
id: diff
uses: somaz94/kube-diff-action@v1
with:
source: file
path: ./manifests/
namespace: production
comment: 'true'
env:
KUBECONFIG: /tmp/kubeconfig
- name: Fail if drift detected
if: steps.diff.outputs.has-changes == 'true'
run: |
echo "Drift detected!"
exit 1- uses: somaz94/kube-diff-action@v1
with:
source: file
path: ./manifests/
namespace: production
ignore-field: metadata.annotations.checksum/config,spec.replicas
context-lines: '5'
exit-code: 'true'- uses: somaz94/kube-diff-action@v1
with:
source: file
path: ./manifests/
namespace: production
diff-strategy: last-applied- name: Get drift report
id: diff
uses: somaz94/kube-diff-action@v1
with:
source: file
path: ./manifests/
output: json
comment: 'false'
- name: Process result
run: echo '${{ steps.diff.outputs.result }}' | jq '.resources[] | select(.status == "changed")'| Input | Description | Required | Default |
|---|---|---|---|
source |
Source type: file, helm, or kustomize |
Yes | |
path |
Path to manifests, chart, or overlay | Yes | |
values |
Helm values files (comma-separated) | No | |
release |
Helm release name | No | release |
namespace |
Filter by namespace | No | |
kind |
Filter by resource kind (comma-separated) | No | |
name |
Filter by resource name (comma-separated) | No | |
selector |
Label selector (e.g., app=nginx,env=prod) |
No | |
output |
Output format: color, plain, json, markdown, table |
No | markdown |
summary-only |
Show summary only | No | false |
ignore-field |
Field paths to ignore in diff (comma-separated, dot notation) | No | |
context-lines |
Number of context lines in diff output | No | 3 |
exit-code |
Report exit-code as 0 even when changes are detected (has-changes still reports drift) |
No | false |
diff-strategy |
Comparison strategy: live or last-applied |
No | live |
comment |
Post result as PR comment | No | true |
version |
kube-diff version to install | No | latest |
token |
GitHub token for PR comments and authenticating the version lookup (raises the GitHub API rate limit) | No | ${{ github.token }} |
| Output | Description |
|---|---|
result |
Full diff output text (kube-diff's stdout; warnings and errors go to the step log) |
exit-code |
0 = no changes, 1 = changes detected, 2 = error |
has-changes |
true if drift was detected, false otherwise |
The action does not fail when drift is detected (exit-code 1). It fails only when kube-diff itself errors, for example on a bad path or an unreachable cluster, and then reports exit-code 2. Use has-changes output to control your workflow:
- name: Fail on drift
if: steps.diff.outputs.has-changes == 'true'
run: exit 1Use kube-diff-action to detect manifest drift and kube-events-action to check cluster warnings β all in one workflow.
name: Deploy & Validate
on:
push:
branches: [main]
jobs:
deploy-and-validate:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Check drift before deploy
id: diff
uses: somaz94/kube-diff-action@v1
with:
source: file
path: ./manifests/
namespace: production
output: markdown
- name: Deploy if drift detected
if: steps.diff.outputs.has-changes == 'true'
run: kubectl apply -f ./manifests/
- name: Check cluster events after deploy
id: events
uses: somaz94/kube-events-action@v1
with:
namespace: production
type: Warning
since: 5m
threshold: '3'
comment: 'true'
- name: Summary
run: |
echo "Drift detected: ${{ steps.diff.outputs.has-changes }}"
echo "Warnings after deploy: ${{ steps.events.outputs.warning-count }}"name: Cluster Health Check
on:
schedule:
- cron: '0 */6 * * *'
jobs:
health-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check manifest drift
id: diff
uses: somaz94/kube-diff-action@v1
with:
source: kustomize
path: ./overlays/production/
output: json
comment: 'false'
- name: Check cluster warnings
id: events
uses: somaz94/kube-events-action@v1
with:
all-namespaces: 'true'
type: Warning
since: 6h
output: json
comment: 'false'
- name: Notify on issues
if: steps.diff.outputs.has-changes == 'true' || steps.events.outputs.has-warnings == 'true'
run: |
curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-d "{\"text\": \"π Cluster issues detected\nDrift: ${{ steps.diff.outputs.has-changes }}\nWarnings: ${{ steps.events.outputs.warning-count }}\"}"- kube-diff: Diff local manifests (plain YAML, Helm, Kustomize) against the live cluster.
- kube-drift: An operator that runs the same manifest-vs-cluster comparison on a schedule, inside the cluster.
This project is licensed under the Apache License 2.0 β see the LICENSE file for details.