GitOps · Automation · Kubernetes

Helm Chart for
pr-generator

Community-maintained chart that deploys pr-generator — an automated pull request daemon for GitHub and Bitbucket Cloud, designed for ArgoCD Image Updater and GitOps workflows.

Quick start
$ helm repo add helm-pr-generator https://devops-ia.github.io/helm-pr-generator
$ helm repo update
$ helm install pr-generator helm-pr-generator/pr-generator \
    --set config.providers.github.owner=my-org \
    --set config.providers.github.repo=my-repo \
    --set config.providers.github.appId="123456" \
    --set secrets.github.privateKey="$(cat github-app.pem)"

Everything you need, out of the box

Production-ready defaults with security hardening, flexible multi-provider support, annotation-based ArgoCD discovery, and built-in Prometheus observability.

GitHub App & PAT

Supports GitHub App (PEM key) and Personal Access Token auth. Switch per provider with authMethod.

Bitbucket Cloud

Full Bitbucket Cloud support via access token. Run GitHub and Bitbucket providers side by side.

Multi-provider

Declare multiple named provider instances — different GitHub orgs, multiple Bitbucket workspaces — all in one deployment.

Security-hardened

Non-root user, read-only filesystem, dropped capabilities, automountServiceAccountToken: false, and dedicated ServiceAccount.

Auto-rollout

Pod annotation checksum/config triggers a rolling restart whenever config.* values change. No manual rollout needed.

PDB & probes

PodDisruptionBudget protects against involuntary eviction. Liveness, readiness, and startup probes are all configurable.

Flexible secrets

Chart-managed inline secrets or existingSecret for External Secrets Operator and Vault integration.

Dry-run mode

Enable config.dryRun: true to simulate PR creation without touching any repository. Safe to test in production clusters.

ArgoCD ready

Designed to pair with ArgoCD Image Updater. Watches branches created by the updater and opens PRs automatically.

Annotation discovery

Each ArgoCD Application declares its own PR rules via annotations. Three modes: config_only, annotations_only, hybrid.

Prometheus metrics

Built-in /metrics endpoint with counters, histograms, and gauges. Pod annotations and ServiceMonitor included.

Add the repository

Helm must be installed. Once set up, add this repository and install the chart.

bash — GitHub App (recommended)
# Add the repository
$ helm repo add helm-pr-generator https://devops-ia.github.io/helm-pr-generator
$ helm repo update

# Install with GitHub App authentication
$ helm install pr-generator helm-pr-generator/pr-generator \
    --namespace pr-generator --create-namespace \
    --set config.providers.github.owner=my-org \
    --set config.providers.github.repo=my-repo \
    --set config.providers.github.appId="123456" \
    --set config.providers.github.installationId="78901234" \
    --set secrets.github.privateKey="$(cat github-app.pem)"
bash — GitHub PAT
$ helm install pr-generator helm-pr-generator/pr-generator \
    --namespace pr-generator --create-namespace \
    --set config.providers.github.authMethod=pat \
    --set config.providers.github.owner=my-org \
    --set config.providers.github.repo=my-repo \
    --set secrets.github.token="ghp_xxxx"
bash — Upgrade
$ helm repo update
$ helm upgrade pr-generator helm-pr-generator/pr-generator

GitOps integration

pr-generator is designed to close the loop when ArgoCD Image Updater pushes a commit to a new branch. Configure it to watch that branch pattern and open the PR automatically.

values.yaml — ArgoCD Image Updater pattern
config:
  scanFrequency: 300
  providers:
    github:
      enabled: true
      owner: my-org
      repo: my-app
      appId: "123456"
      installationId: "78901234"

  rules:
    - pattern: "argocd-image-updater-.*-dev-.*"   # regex matching Image Updater branches
      destinations:
        github: develop
    - pattern: "argocd-image-updater-.*-pro-.*"
      destinations:
        github: main

secrets:
  github:
    existingSecret: github-app-secret   # key: private-key.pem
Tip: Set config.dryRun: true first to verify branch matching before enabling live PR creation. The daemon logs which branches matched each rule on every scan cycle.

Per-app rule discovery

Let each ArgoCD Application declare its own PR rules via annotations — no central config.rules change needed when onboarding a new application. Mirrors the model used by ArgoCD Image Updater.

values.yaml — enable annotation discovery
annotationDiscovery:
  enabled: true
  mode: hybrid           # config_only | annotations_only | hybrid
  annotationPrefix: "pr-generator.io"
ArgoCD Application — annotation schema
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
  annotations:
    pr-generator.io/enabled: "true"
    pr-generator.io/pattern: "argocd-image-updater-.*-dev-.*"
    pr-generator.io/destination.github: "develop"
    pr-generator.io/destination.bitbucket: "dev"
RBAC: Enabling annotation discovery automatically creates a ClusterRole and ClusterRoleBinding granting the chart ServiceAccount get/list on applications.argoproj.io cluster-wide. Requires serviceAccount.create: true.

Prometheus metrics

Built-in /metrics endpoint (Prometheus format) on the health port (8080). Enabled by default — no extra configuration required for basic scraping.

MetricTypeDescription
pr_generator_prs_created_totalCounterPRs successfully created, labelled by provider and destination
pr_generator_prs_skipped_totalCounterPRs skipped because one already exists
pr_generator_prs_failed_totalCounterPR creation failures, labelled by error type
pr_generator_scan_cycles_totalCounterCompleted scan cycles by result (success / partial / error)
pr_generator_scan_duration_secondsHistogramWall-clock duration of each scan cycle
pr_generator_rules_activeGaugeActive scan rules at end of last cycle
pr_generator_annotation_rules_discoveredGaugeRules discovered from ArgoCD Application annotations
pr_generator_provider_errors_totalCounterProvider-level errors by provider and error type
pr_generator_upGauge1 after first successful scan cycle, 0 on startup
bash — scrape metrics manually
$ kubectl port-forward svc/pr-generator 8080:8080 -n pr-generator
$ curl -s http://localhost:8080/metrics | grep pr_generator
values.yaml — Prometheus Operator ServiceMonitor
metrics:
  enabled: true
  serviceMonitor:
    enabled: true        # requires Prometheus Operator CRDs
    interval: 30s
    scrapeTimeout: 10s
    labels:
      release: kube-prometheus-stack   # must match serviceMonitorSelector

Key values

Common values to get started. Full reference at values.yaml.

Parameter Description Default
config.scanFrequency Seconds between branch scan cycles. 300
config.dryRun Simulate PR creation without modifying repositories. false
config.providers.github.enabled Enable the GitHub provider instance. false
config.providers.github.authMethod app (GitHub App PEM) or pat (Personal Access Token). "app"
config.providers.github.owner GitHub org or user that owns the repository. ""
config.providers.github.repo Repository name (without org prefix). ""
config.providers.github.appId GitHub App ID (string). Required when authMethod: app. ""
config.rules List of rules: pattern (regex) + destinations map of provider-key → branch. Can be empty when annotationDiscovery.mode: annotations_only. []
secrets.github.existingSecret Name of an existing Secret with the GitHub credential key. Shared by all GitHub providers. ""
secrets.bitbucket.existingSecret Name of an existing Secret with the Bitbucket credential key. Shared by all Bitbucket providers. ""
annotationDiscovery.enabled Read ArgoCD Application CRs to derive scan rules. Creates ClusterRole + ClusterRoleBinding automatically. false
annotationDiscovery.mode config_only, annotations_only, or hybrid. "hybrid"
metrics.enabled Expose /metrics (Prometheus format) on the health port. Adds pod scrape annotations. true
metrics.serviceMonitor.enabled Create a Prometheus Operator ServiceMonitor. Requires Operator CRDs installed. false
serviceAccount.create Create a dedicated ServiceAccount with automountServiceAccountToken: false. true
podDisruptionBudget.enabled Create a PodDisruptionBudget to protect against involuntary eviction. false
resources Pod resource requests and limits. {}
image.tag Image tag. Defaults to chart appVersion when empty. ""

Common issues

Most issues come down to auth configuration or branch pattern matching.

No PRs being created — branches exist but nothing happens

Check the daemon logs first:

kubectl logs -n pr-generator deploy/pr-generator -f

Common causes:

  • Branch pattern does not match. Test your regex against the actual branch name.
  • config.dryRun is true — PRs are simulated but not created.
  • The provider is disabled (enabled: false).
  • A PR already exists for that branch — the daemon skips duplicates.
GitHub App: "could not read private key" or "installation not found"

Two separate issues:

  • Private key not found: confirm the Secret key name matches privateKeyKey (default: privateKey). The PEM must be the complete RSA key including header/footer lines.
  • Installation not found: installationId must be the numeric ID shown on the GitHub App installation settings page, not the App ID.
kubectl get secret <github-secret> -o jsonpath='{.data.privateKey}' | base64 -d | head -1
# should print: -----BEGIN RSA PRIVATE KEY-----
Multi-provider: duplicate tokenEnv error on startup

Each provider of the same type must use a unique tokenEnv value. If two Bitbucket providers share the same env var name, the daemon raises a ValueError at startup to prevent silent credential overwrite.

config:
  providers:
    bitbucket:
      tokenEnv: BITBUCKET_TOKEN        # provider 1
    bitbucket-org2:
      type: bitbucket
      tokenEnv: BITBUCKET_ORG2_TOKEN   # must be different
Helm schema validation: appId must be a string

The values.schema.json requires appId and installationId to be strings. When using --set on the CLI, Helm auto-parses numeric values as integers. Use quotes:

helm install ... --set config.providers.github.appId="123456"
#                                                          ^     ^
#                                                      quoted string

Or use a values file where YAML quotes are explicit.