GitHub Integration
Diverge provides first-class GitHub integration for automatic preview environments on every Pull Request. This guide covers webhook setup, GitHub Actions workflows with change detection, sticky PR comments, and merge gating.
1. Controller Setup
Section titled “1. Controller Setup”Install the Diverge controller with GitHub notifier enabled:
helm repo add diverge https://divergedev.github.io/divergehelm repo update
helm install diverge diverge/diverge \ --namespace diverge-system \ --create-namespace \ --set notifierProvider=github \ --set controller.env.DIVERGE_NOTIFIER_TOKEN=<github-token> \ --set controller.env.DIVERGE_WEBHOOK_SECRET=<webhook-secret>Required Environment Variables
Section titled “Required Environment Variables”| Variable | Description |
|---|---|
DIVERGE_NOTIFIER_TOKEN |
GitHub personal access token or fine-grained token with issues:write and statuses:write |
DIVERGE_WEBHOOK_SECRET |
Shared secret for HMAC-SHA256 webhook signature validation |
2. Webhook Configuration
Section titled “2. Webhook Configuration”Navigate to your GitHub repository or organization: Settings → Webhooks → Add webhook.
| Setting | Value |
|---|---|
| Payload URL | https://diverge.yourdomain.com/github-webhook |
| Content type | application/json |
| Secret | Your DIVERGE_WEBHOOK_SECRET value |
Events to Select
Section titled “Events to Select”Select Let me select individual events, and check:
- ✅ Pull requests (covers
opened,synchronize,reopened,closed)
For PreviewGroups (multi-service), add a second webhook:
| Setting | Value |
|---|---|
| Payload URL | https://diverge.yourdomain.com/github-previewgroup-webhook |
Webhook Events Handled
Section titled “Webhook Events Handled”| PR Action | Diverge Behavior |
|---|---|
opened, reopened |
Creates Environment / PreviewGroup CR |
synchronize (new push) |
Updates the CR with new commit SHA |
closed |
Deletes the CR (triggers teardown) |
3. GitHub Actions Workflow
Section titled “3. GitHub Actions Workflow”Diverge provides a complete GitHub Actions workflow for automated preview environments. Add this to .github/workflows/diverge-preview.yml:
Pipeline Overview
Section titled “Pipeline Overview”name: Diverge Preview Environment
on: pull_request: types: [opened, synchronize, reopened, closed]
concurrency: group: diverge-preview-${{ github.head_ref || github.ref }} cancel-in-progress: true
permissions: contents: read pull-requests: writeChange Detection
Section titled “Change Detection”Use diverge diff to detect which services changed based on git diff and .diverge.yaml path mappings:
- name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 # Full history required for accurate diffing
- name: Install Diverge CLI uses: divergedev/setup-diverge@v1
- name: Detect changed services id: diff run: | DIFF_JSON=$(diverge diff --output json --base origin/main) SERVICES=$(echo "$DIFF_JSON" | jq -r '.services // [] | join(", ")') COUNT=$(echo "$DIFF_JSON" | jq -r '.count // 0') echo "changed_services=${SERVICES}" >> $GITHUB_OUTPUT echo "changed_count=${COUNT}" >> $GITHUB_OUTPUTRoute Tracing
Section titled “Route Tracing”Trace the ingress path for each changed service to understand request routing:
- name: Trace request routes id: routes run: | IFS=', ' read -ra SERVICES <<< "${{ steps.diff.outputs.changed_services }}" for svc in "${SERVICES[@]}"; do echo "::group::Route trace for $svc" diverge route "$svc" echo "::endgroup::" doneDeploy with Sticky PR Comment
Section titled “Deploy with Sticky PR Comment”Create the preview environment and post a summary comment on the PR:
- name: Deploy preview environment id: deploy env: PR_NUMBER: ${{ github.event.pull_request.number }} run: | ENV_NAME="preview-mr-${PR_NUMBER}" DEPLOY_OUTPUT=$(diverge create --mr "${PR_NUMBER}") echo "$DEPLOY_OUTPUT"
PREVIEW_URL=$(echo "$DEPLOY_OUTPUT" | grep -o 'https://[^ ]*' | head -n 1) if [ -z "$PREVIEW_URL" ]; then PREVIEW_URL="https://${ENV_NAME}.preview.example.com" fi
echo "env_name=${ENV_NAME}" >> $GITHUB_OUTPUT echo "preview_url=${PREVIEW_URL}" >> $GITHUB_OUTPUT
- name: Comment Preview URL on PR uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const commentIdentifier = '<!-- diverge-preview-comment -->'; const body = `${commentIdentifier} ## 🚀 Diverge Preview Environment
| Property | Value | |---|---| | **Environment** | \`${{ steps.deploy.outputs.env_name }}\` | | **Preview URL** | [${{ steps.deploy.outputs.preview_url }}](${{ steps.deploy.outputs.preview_url }}) | | **Changed Services** | \`${{ steps.diff.outputs.changed_services }}\` | | **Deploy Mode** | \`delta\` |
*Updated for commit \`${context.sha.substring(0, 7)}\`.*`;
const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, });
const existing = comments.find(c => c.body?.includes(commentIdentifier)); if (existing) { await github.rest.issues.updateComment({ ...context.repo, comment_id: existing.id, body, }); } else { await github.rest.issues.createComment({ ...context.repo, issue_number: context.issue.number, body, }); }The <!-- diverge-preview-comment --> HTML marker ensures only one comment is maintained per PR — subsequent pushes update the existing comment.
Automatic Cleanup
Section titled “Automatic Cleanup”Tear down environments when the PR is closed or merged:
cleanup: name: Teardown Preview Environment if: github.event.action == 'closed' runs-on: ubuntu-latest steps: - uses: divergedev/setup-diverge@v1 - name: Delete preview environment run: | diverge delete "preview-mr-${{ github.event.pull_request.number }}" || trueThe setup-diverge Action
Section titled “The setup-diverge Action”The divergedev/setup-diverge action handles CLI installation and caching automatically:
- uses: divergedev/setup-diverge@v1To pin a specific version:
- uses: divergedev/setup-diverge@v1 with: version: '0.8.2'It resolves the latest release via GitHub API, caches the binary per OS/arch/version using actions/cache, and adds diverge to PATH.
4. Merge Gating with Commit Statuses
Section titled “4. Merge Gating with Commit Statuses”Diverge posts diverge/preview commit statuses to GitHub, enabling required status checks.
Status Transitions
Section titled “Status Transitions”| Status | Meaning |
|---|---|
pending |
Environment is being provisioned |
success |
Environment is healthy and serving traffic |
failure |
An error occurred during provisioning |
error |
Environment was canceled |
Requiring the Check
Section titled “Requiring the Check”- Go to Settings → Branches → Branch protection rules
- Check Require status checks to pass before merging
- Search for and add
diverge/preview
5. PR Comments
Section titled “5. PR Comments”Diverge automatically posts and updates Pull Request comments through the environment lifecycle:
| Event | Comment Content |
|---|---|
| Created | Services being deployed, routing header |
| Ready | Preview URL, curl command with routing header |
| Failed | Error details, failed conditions, controller log hints |
| Teardown | Cleanup confirmation and reason |
Comments are deduplicated — Diverge tracks the comment ID and updates the existing comment on subsequent events.
6. GitHub Actions Environment Variables
Section titled “6. GitHub Actions Environment Variables”Standard GitHub Actions variables used in Diverge workflows:
| Variable | Used For |
|---|---|
github.event.pull_request.number |
PR number for diverge create --mr |
github.head_ref |
Source branch for concurrency grouping |
github.event.action |
Cleanup trigger (closed) |
context.sha |
Commit hash for comment updates |
secrets.KUBECONFIG |
Kubernetes cluster credentials |
secrets.GITHUB_TOKEN |
PR comment posting (auto-provided) |
7. Feature Parity
Section titled “7. Feature Parity”| Feature | GitHub | GitLab |
|---|---|---|
| Webhook authentication | HMAC-SHA256 | Token (constant-time) |
| MR/PR comments | ✅ Sticky comments | ✅ Sticky notes |
| Commit statuses | ✅ diverge/preview |
✅ diverge/preview |
| PreviewGroup support | ✅ | ✅ |
Change detection (diverge diff) |
✅ | ✅ |
Route tracing (diverge route) |
✅ | ✅ |
| Merge gating | ✅ Branch protection | ✅ Protected branches |
| Config fetching | ✅ Contents API | ✅ Repository Files API |
| CLI install + caching | ✅ setup-diverge@v1 |
✅ diverge-cli Docker image |
| Self-hosted support | ✅ | ✅ (--gitlab-url) |
Troubleshooting
Section titled “Troubleshooting”| Issue | Solution |
|---|---|
| No PR comments | Verify workflow has pull-requests: write permission |
| Webhook 404 | Check payload URL matches /github-webhook |
| Webhook signature failures | Ensure DIVERGE_WEBHOOK_SECRET matches the GitHub webhook secret |
| Status checks not appearing | Verify DIVERGE_NOTIFIER_TOKEN has statuses:write scope |
| Stale PR comments | Check the <!-- diverge-preview-comment --> marker is present |
