Skip to content

PullRequest Resources

The PullRequest resource automates the creation and management of pull requests based on Kubernetes cluster data. This guide covers everything you need to know about using PullRequest resources for GitOps workflows and automated change management.

Overview

PullRequest resources enable you to: - Automatically create pull requests when cluster resources change - Implement change approval workflows through Git-based review processes - Sync cluster state to Git repositories with proper change tracking - Integrate with CI/CD pipelines triggered by pull request events

Basic PullRequest Resource

Minimal Example

apiVersion: gco.galos.one/v1
kind: PullRequest
metadata:
  name: simple-pr
  namespace: default
spec:
  repository: "https://github.com/myorg/config-repo.git"
  baseBranch: "main"
  headBranch: "automated-update"
  title: "Automated configuration update"
  body: "This PR contains automated updates from the Kubernetes cluster"
  resourceRef:
    apiVersion: v1
    kind: ConfigMap
    name: app-config
    namespace: default
    path: "config/app.yaml"

This creates a pull request with the contents of the app-config ConfigMap.

Complete Example

apiVersion: gco.galos.one/v1
kind: PullRequest
metadata:
  name: comprehensive-pr
  namespace: default
  labels:
    app: myapp
    automation: enabled
spec:
  # Repository configuration
  repository: "https://github.com/myorg/config-repo.git"
  baseBranch: "main"
  headBranch: "config-update-{{ .timestamp | date "20060102-150405" }}"

  # PR metadata
  title: "🤖 Automated configuration update from {{ .cluster.name }}"
  body: |
    # Automated Configuration Update

    This pull request contains automated configuration updates from the Kubernetes cluster `{{ .cluster.name }}`.

    ## Changes Summary
    - **Resource**: {{ .resourceRef.kind }}/{{ .resourceRef.name }}
    - **Namespace**: {{ .resourceRef.namespace }}
    - **Updated**: {{ .timestamp | date "2006-01-02 15:04:05 UTC" }}

    ## Resource Details
    ```yaml
    {{ .resourceContent | indent 4 }}
    ```

    ## Validation Checklist
    - [ ] Configuration syntax is valid
    - [ ] No sensitive data is exposed
    - [ ] Changes are backwards compatible
    - [ ] All required fields are present

    ## Approval Required
    This PR requires approval from the configuration management team before merging.

    ---
    *Generated by git-change-operator on {{ .timestamp }}*

  # Labels and metadata  
  labels:
    - "automated"
    - "configuration"
    - "cluster-sync"
    - "{{ .cluster.environment }}"

  # Review assignments
  assignees:
    - "config-team"
    - "{{ .resourceRef.owner | default "devops-team" }}"
  reviewers:
    - "senior-engineers"
  teamReviewers:
    - "platform-team"

  # PR options
  draft: false
  maintainerCanModify: true

  # Authentication
  credentials:
    secretName: github-token
    tokenKey: token

  # Resource reference (same options as GitCommit)
  resourceRef:
    apiVersion: v1
    kind: ConfigMap
    name: app-config
    namespace: default
    path: "applications/myapp/config.yaml"
    strategy: "template"
    template: |
      # Application Configuration
      # Source: {{ .metadata.namespace }}/{{ .metadata.name }}
      # Generated: {{ .timestamp }}

      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: {{ .metadata.name }}
        namespace: {{ .metadata.namespace }}
        labels:
      {{ range $key, $value := .metadata.labels }}
          {{ $key }}: {{ $value }}
      {{ end }}
        annotations:
          generated-by: git-change-operator
          source-cluster: {{ .cluster.name }}
          sync-timestamp: "{{ .timestamp }}"
      data:
      {{ range $key, $value := .data }}
        {{ $key }}: |
      {{ $value | indent 4 }}
      {{ end }}

  # Write configuration
  writeMode: "overwrite"
  fileMode: "0644"
  createDirs: true

  # Reconciliation settings
  reconcileInterval: "300s"
  suspend: false

  # Auto-merge settings (optional)
  autoMerge:
    enabled: false
    strategy: "squash"  # merge, squash, rebase
    conditions:
      - allChecksPass: true
      - reviewApproved: true
      - upToDate: true

Pull Request Lifecycle

Creation Process

  1. Resource Change Detection - Operator detects changes to referenced resources
  2. Branch Creation - Creates or updates the head branch with new content
  3. PR Creation - Creates pull request if it doesn't exist
  4. PR Update - Updates existing PR with new content if needed
  5. Status Tracking - Monitors PR status and reports back to Kubernetes

Branch Management

PullRequest resources can manage branches automatically:

spec:
  # Dynamic branch naming
  headBranch: "auto-update/{{ .resourceRef.name }}/{{ .timestamp | date "20060102" }}"

  # Branch creation options
  branchOptions:
    createFromBase: true     # Create branch from base branch
    updateIfExists: true     # Update existing branch
    deleteAfterMerge: true   # Auto-delete branch after PR merge
    protectBranch: false     # Don't add branch protection

Status Management

Monitor PullRequest resource status:

# Check PullRequest status
kubectl get pullrequest mypr -o yaml

# Example status
status:
  phase: "Open"  # Pending, Open, Merged, Closed, Failed
  prNumber: 123
  prURL: "https://github.com/myorg/config-repo/pull/123"
  headSHA: "abc123def456"
  baseSHA: "def456abc123"
  conditions:
  - type: "Ready"
    status: "True"
    lastTransitionTime: "2024-01-15T10:30:00Z"
    reason: "PRCreated"
    message: "Pull request created successfully"
  - type: "ReviewsApproved"
    status: "False"
    reason: "PendingReview"
    message: "Waiting for required reviews"
  lastUpdateTime: "2024-01-15T10:30:00Z"
  observedGeneration: 1

Authentication and Permissions

GitHub Token Authentication

# GitHub personal access token
apiVersion: v1
kind: Secret
metadata:
  name: github-token
  namespace: default
type: Opaque
data:
  token: Z2hwX3Rva2VuX2hlcmU=  # base64 encoded GitHub token

---
# PullRequest using GitHub token
apiVersion: gco.galos.one/v1
kind: PullRequest
metadata:
  name: github-pr
spec:
  repository: "https://github.com/myorg/repo.git"
  credentials:
    secretName: github-token
    tokenKey: token

Required GitHub Token Permissions

For public repositories: - public_repo - Access to public repositories - workflow - Update GitHub Actions workflows (if needed)

For private repositories: - repo - Full repository access - workflow - Update GitHub Actions workflows (if needed)

GitLab Token Authentication

# GitLab access token
apiVersion: v1
kind: Secret
metadata:
  name: gitlab-token
  namespace: default
type: Opaque
data:
  token: Z2xwYXRfdG9rZW5faGVyZQ==  # base64 encoded GitLab token

---
# PullRequest for GitLab (merge request)
apiVersion: gco.galos.one/v1
kind: PullRequest  
metadata:
  name: gitlab-mr
spec:
  repository: "https://gitlab.com/myorg/repo.git"
  provider: "gitlab"  # Specify GitLab provider
  credentials:
    secretName: gitlab-token
    tokenKey: token

Advanced Features

Multiple Resource References

Include multiple resources in a single PR:

spec:
  resourceRefs:  # Note: plural form
    - apiVersion: v1
      kind: ConfigMap
      name: app-config
      namespace: default
      path: "config/app.yaml"
    - apiVersion: v1
      kind: Secret
      name: app-secrets
      namespace: default
      path: "secrets/app.yaml"
      strategy: "template"
      template: |
        # Secret metadata (values redacted for security)
        apiVersion: v1
        kind: Secret
        metadata:
          name: {{ .metadata.name }}
          namespace: {{ .metadata.namespace }}
        type: {{ .type }}
        data:
        {{ range $key, $value := .data }}
          {{ $key }}: "[REDACTED-{{ $value | len }}-bytes]"
        {{ end }}
    - apiVersion: apps/v1
      kind: Deployment
      name: myapp
      namespace: default
      path: "deployments/myapp.yaml"

Conditional PR Creation

Create PRs based on conditions:

spec:
  # Only create PR if conditions are met
  conditions:
    - field: ".metadata.labels.environment"
      operator: "equals"
      value: "production"
    - field: ".spec.replicas"
      operator: "greaterThan"
      value: 1

  # Template with conditional logic
  body: |
    # Configuration Update

    {{ if eq .metadata.labels.environment "production" }}
    ⚠️ **PRODUCTION CHANGE** - This change affects the production environment.

    Additional review required from:
    - Platform Engineering Team
    - Security Team
    {{ end }}

    ## Resource Details
    - **Name**: {{ .metadata.name }}
    - **Namespace**: {{ .metadata.namespace }}
    - **Environment**: {{ .metadata.labels.environment }}

Auto-Merge Configuration

Configure automatic merging for trusted changes:

spec:
  autoMerge:
    enabled: true
    strategy: "squash"  # merge, squash, rebase

    # Conditions that must be met before auto-merge
    conditions:
      - allChecksPass: true
      - reviewApproved: true
      - upToDate: true
      - noConflicts: true

    # Additional requirements
    requirements:
      minApprovals: 2
      dismissStaleReviews: true
      requireCodeOwnerReviews: true
      requiredStatusChecks:
        - "continuous-integration"
        - "security-scan"
        - "config-validation"

    # Auto-merge delays
    delays:
      afterCreation: "30m"   # Wait 30 minutes after creation
      afterUpdate: "10m"     # Wait 10 minutes after updates
      afterApproval: "5m"    # Wait 5 minutes after final approval

File Encryption

Encrypt sensitive files before committing them to the repository using age encryption:

spec:
  # Basic encryption setup with SSH public key
  encryption:
    enabled: true
    recipients:
      - type: ssh
        secretRef:
          name: ssh-keys
          key: id_rsa.pub

  files:
    - path: "secrets/database.yaml"
      content: |
        database:
          host: db.example.com
          password: super-secret-password
          ssl_cert: |
            -----BEGIN CERTIFICATE-----
            MIIBkTCB+wIJANfKvPOD7JEBMA0GCSqGSIb3DQEBBQUAMBkx...
            -----END CERTIFICATE-----

Advanced encryption with multiple recipient types:

spec:
  encryption:
    enabled: true
    fileExtension: ".encrypted"  # Custom extension (default: .age)
    recipients:
      # Age key recipient
      - type: age
        secretRef:
          name: age-keys
          key: public-key

      # SSH key recipient (uses SSH public key)
      - type: ssh
        secretRef:
          name: ssh-keys
          key: id_rsa.pub

      # Passphrase recipient
      - type: passphrase
        secretRef:
          name: passwords
          key: encryption-passphrase

      # YubiKey recipient (hardware security key)
      - type: yubikey
        secretRef:
          name: yubikey-piv
          key: public-key

  resourceRefs:
    - apiVersion: v1
      kind: Secret
      name: app-secrets
      namespace: default
      path: "secrets/app-secrets.yaml"
      # This will be encrypted as secrets/app-secrets.yaml.encrypted

Encryption secrets setup:

# Create age key secret
apiVersion: v1
kind: Secret
metadata:
  name: age-keys
  namespace: default
data:
  public-key: YWdlMXh4eGJ4eGJ4eGJ4eGJ4eGJ4eGJ4eGJ4eGJ4eGJ4eGJ4...

---
# Create SSH key secret
apiVersion: v1
kind: Secret
metadata:
  name: ssh-keys  
  namespace: default
data:
  id_rsa.pub: c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCZ1FD...

---
# Create passphrase secret
apiVersion: v1
kind: Secret
metadata:
  name: passwords
  namespace: default
data:
  encryption-passphrase: bXktc2VjdXJlLXBhc3NwaHJhc2U=  # my-secure-passphrase

---
# Create YubiKey secret
apiVersion: v1
kind: Secret
metadata:
  name: yubikey-piv
  namespace: default
data:
  public-key: c2stcHV0dHk6QUFBQUIzTnphQzF5YzJFQUFBQURBUUFCQUFBQmdRRHo...

Benefits of encryption: - Security: Sensitive data is encrypted before being stored in Git - Compliance: Meet security requirements for storing secrets in repositories - Flexibility: Support for multiple encryption methods (age keys, SSH keys, passphrases, YubiKeys) - GitOps-ready: Encrypted files can be safely stored in public repositories

Complete Encryption Examples

Basic Encrypted PullRequest

apiVersion: gco.galos.one/v1
kind: PullRequest
metadata:
  name: encrypted-config-pr
  namespace: default
spec:
  repository: "https://github.com/myorg/secure-configs.git"
  baseBranch: "main"
  headBranch: "encrypted-secrets-update"
  title: "Update encrypted application secrets"
  body: |
    Automated update of encrypted configuration files.

    Files are encrypted using age encryption for secure storage.

    **Files Updated:**
    - Database credentials
    - API keys
    - TLS certificates

  authSecretRef: "git-credentials"

  # Encryption Configuration
  encryption:
    enabled: true
    fileExtension: ".encrypted"  # Files will be saved as .encrypted
    recipients:
      - type: ssh
        secretRef:
          name: ssh-keys
          key: id_rsa.pub

  files:
    - path: "secrets/database.yaml"
      content: |
        database:
          host: "production-db.example.com"
          username: "app_user"
          password: "super-secret-password-123"
          ssl_mode: "require"

    - path: "secrets/api-keys.yaml"
      content: |
        api:
          stripe_key: "sk_live_abcdefghijklmnop"
          sendgrid_key: "SG.xyz123.abc456"
          oauth_secret: "oauth_secret_token_xyz"

Multi-Recipient Encryption with YubiKey

apiVersion: gco.galos.one/v1
kind: PullRequest
metadata:
  name: multi-encryption-pr
  namespace: default
spec:
  repository: "https://github.com/myorg/enterprise-config.git"
  baseBranch: "main"
  headBranch: "multi-encrypted-update"
  title: "Multi-encrypted configuration update"

  authSecretRef: "git-credentials"

  # Multiple Encryption Recipients
  encryption:
    enabled: true
    recipients:
      # Team SSH keys
      - type: ssh
        secretRef:
          name: team-ssh-keys
          key: devops-team.pub

      # Hardware security keys
      - type: yubikey
        secretRef:
          name: security-team-yubikeys
          key: security-officer.pub

      # Emergency age key
      - type: age
        secretRef:
          name: emergency-keys
          key: break-glass-key

      # Backup passphrase
      - type: passphrase
        secretRef:
          name: backup-auth
          key: recovery-passphrase

  # Reference existing Kubernetes resources
  resourceReferences:
    - apiVersion: v1
      kind: Secret
      name: app-secrets
      namespace: production
      strategy: dump
      output:
        path: "production/secrets/app-secrets.yaml"
        # Will be encrypted as: production/secrets/app-secrets.yaml.encrypted

    - apiVersion: v1
      kind: ConfigMap
      name: database-config
      namespace: production
      strategy: fields
      output:
        path: "production/config/"
        # Each field becomes a separate encrypted file

Encrypted Resource Backup

apiVersion: gco.galos.one/v1
kind: PullRequest
metadata:
  name: backup-secrets-pr
  namespace: backup-system
spec:
  repository: "https://github.com/myorg/cluster-backups.git"
  baseBranch: "main"
  headBranch: "automated-backup"
  title: "Automated encrypted backup of cluster secrets"

  authSecretRef: "backup-git-credentials"

  encryption:
    enabled: true
    recipients:
      - type: yubikey
        secretRef:
          name: backup-yubikey
          key: backup-officer.pub

  # Backup multiple secrets across namespaces
  resourceReferences:
    # Production secrets
    - apiVersion: v1
      kind: Secret
      name: database-credentials
      namespace: production
      strategy: dump
      output:
        path: "backups/production/database-credentials.yaml"

    # Staging secrets  
    - apiVersion: v1
      kind: Secret
      name: api-tokens
      namespace: staging
      strategy: dump
      output:
        path: "backups/staging/api-tokens.yaml"

    # Certificate secrets
    - apiVersion: v1
      kind: Secret
      name: tls-certificates
      namespace: cert-manager
      strategy: fields
      output:
        path: "backups/certificates/"
        # Each certificate becomes a separate encrypted file

Setting Up Encryption Secrets

# 1. SSH Key Secret
kubectl create secret generic ssh-keys \
  --from-file=id_rsa.pub=~/.ssh/id_rsa.pub \
  --namespace=default

# 2. YubiKey Secret (extract public key from YubiKey PIV)
kubectl create secret generic yubikey-piv \
  --from-literal=public-key="$(ykman piv keys export 9a - | ssh-keygen -i -m PKCS8 -f /dev/stdin)" \
  --namespace=default

# 3. Age Key Secret  
age-keygen -o age-key.txt
kubectl create secret generic age-keys \
  --from-file=public-key=<(grep 'public key:' age-key.txt | cut -d: -f2 | tr -d ' ') \
  --namespace=default

# 4. Passphrase Secret
kubectl create secret generic passwords \
  --from-literal=encryption-passphrase="my-secure-recovery-phrase" \
  --namespace=default

PR Templates and Customization

Dynamic PR Titles

spec:
  title: |
    {{ if eq .resourceRef.kind "Secret" }}🔐{{ else if eq .resourceRef.kind "ConfigMap" }}⚙️{{ else }}📝{{ end }} 
    {{ .resourceRef.kind }}/{{ .resourceRef.name }} update in {{ .resourceRef.namespace }}
    {{- if .metadata.labels.environment }} ({{ .metadata.labels.environment }}){{ end }}

Rich PR Bodies

spec:
  body: |
    # 🤖 Automated Resource Update

    ## Summary
    This PR updates the **{{ .resourceRef.kind }}** `{{ .resourceRef.name }}` in namespace `{{ .resourceRef.namespace }}`.

    ## Resource Information
    | Field | Value |
    |-------|-------|
    | **Kind** | {{ .resourceRef.kind }} |
    | **Name** | {{ .resourceRef.name }} |
    | **Namespace** | {{ .resourceRef.namespace }} |
    | **Cluster** | {{ .cluster.name }} |
    | **Environment** | {{ .metadata.labels.environment | default "unknown" }} |
    | **Updated** | {{ .timestamp | date "2006-01-02 15:04:05 UTC" }} |

    ## Changes
    {{ if .diff }}
    ```diff
    {{ .diff }}
    ```
    {{ else }}
    New resource - no previous version available.
    {{ end }}

    ## Validation
    - [x] Resource syntax is valid
    - [x] All required fields are present  
    - [ ] Manual review completed
    - [ ] Security implications assessed

    {{ if eq .metadata.labels.environment "production" }}
    ## ⚠️ Production Impact
    This change affects the **production** environment. Please ensure:
    1. Change has been tested in staging
    2. Rollback plan is ready
    3. Monitoring is in place
    4. Team is notified
    {{ end }}

    ---
    <details>
    <summary>🔍 Resource Details</summary>

    ```yaml
    {{ .resourceContent | indent 4 }}
    ```
    </details>

    *Automated by git-change-operator • [Documentation](https://github.com/myorg/git-change-operator/docs)*

Provider-Specific Configuration

GitHub Configuration

spec:
  provider: "github"  # Default

  # GitHub-specific options
  github:
    # Organization settings
    organization: "myorg"

    # PR settings
    allowMaintainerEdit: true
    deleteHeadBranch: true

    # Draft PR settings
    draft: false

    # Issue linking
    linkedIssues:
      - number: 123
        action: "closes"  # closes, fixes, resolves

GitLab Configuration

spec:
  provider: "gitlab"

  # GitLab-specific options (merge requests)
  gitlab:
    # Project settings
    projectID: 12345

    # Merge request settings
    removeSourceBranch: true
    squash: true

    # Approval settings
    approvalsRequired: 2

    # Milestone and labels
    milestoneID: 5
    labels: ["automated", "config-sync"]

Bitbucket Configuration

spec:
  provider: "bitbucket"

  # Bitbucket-specific options
  bitbucket:
    # Workspace settings
    workspace: "myworkspace"

    # PR settings
    closeSourceBranch: true

    # Default reviewers
    defaultReviewers:
      - "platform-team"

Monitoring and Observability

Metrics

Monitor PullRequest operations:

# Total PullRequest operations
git_change_operator_pullrequest_operations_total

# PR creation success rate
rate(git_change_operator_pullrequest_operations_total{status="success",operation="create"}[5m])

# Average PR processing time
git_change_operator_pullrequest_duration_seconds

# Open PRs by namespace
git_change_operator_pullrequest_open_total

Events

Monitor PullRequest events:

# View PullRequest events
kubectl describe pullrequest mypr

# Example events
Events:
  Type    Reason              Age   From                        Message
  ----    ------              ----  ----                        -------
  Normal  BranchCreated       5m    pullrequest-controller      Created branch 'auto-update-20240115'
  Normal  ResourceFetched     5m    pullrequest-controller      Successfully fetched resource data
  Normal  FileCommitted       4m    pullrequest-controller      Committed file to branch
  Normal  PRCreated          4m    pullrequest-controller      Pull request created: #123
  Normal  PRUpdated          2m    pullrequest-controller      Pull request updated with latest changes

Alerting

Set up alerts for PullRequest issues:

# Prometheus alert rules
groups:
- name: git-change-operator-pullrequest
  rules:
  - alert: PullRequestCreationFailed
    expr: increase(git_change_operator_pullrequest_operations_total{status="failed",operation="create"}[5m]) > 0
    for: 0m
    labels:
      severity: warning
    annotations:
      summary: "PullRequest creation failed"
      description: "Failed to create pull request for {{ $labels.pullrequest }}"

  - alert: PullRequestStale  
    expr: (time() - git_change_operator_pullrequest_last_update_time) > 604800  # 1 week
    for: 0m
    labels:
      severity: info
    annotations:
      summary: "PullRequest is stale"
      description: "PullRequest {{ $labels.pullrequest }} has not been updated for over 1 week"

Security Considerations

Token Permissions

Minimize required permissions:

# GitHub token with minimal permissions
# For public repos: public_repo
# For private repos: repo
# Additional: workflow (if updating GitHub Actions)

# Store token securely
apiVersion: v1
kind: Secret
metadata:
  name: github-token-minimal
  namespace: git-change-operator-system
type: Opaque
data:
  token: <base64-encoded-token>

Sensitive Data Protection

Prevent sensitive data in PRs:

spec:
  resourceRef:
    apiVersion: v1
    kind: Secret
    name: app-secrets
    path: "secrets/app-secrets.yaml"
    strategy: "template"
    template: |
      # Sanitized secret for PR review
      apiVersion: v1
      kind: Secret
      metadata:
        name: {{ .metadata.name }}
        namespace: {{ .metadata.namespace }}
      type: {{ .type }}
      data:
      {{ range $key, $value := .data }}
        {{ $key }}: "[REDACTED]"  # Don't expose actual values
      {{ end }}

      # Metadata for reviewers
      _metadata:
        originalKeys: {{ keys .data | join ", " }}
        keyCount: {{ len .data }}
        lastUpdated: {{ .timestamp }}

RBAC Configuration

# Minimal RBAC for PullRequest operations
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pullrequest-operator
rules:
# PullRequest resources
- apiGroups: ["gco.galos.one"]
  resources: ["pullrequests"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["gco.galos.one"]  
  resources: ["pullrequests/status"]
  verbs: ["get", "update", "patch"]

# Referenced resources (minimal read access)
- apiGroups: [""]
  resources: ["configmaps", "secrets"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets", "statefulsets"]
  verbs: ["get", "list", "watch"]

Use Cases and Patterns

Configuration Review Workflow

Implement a configuration review process:

apiVersion: gco.galos.one/v1
kind: PullRequest
metadata:
  name: config-review-workflow
spec:
  repository: "https://github.com/myorg/k8s-configs.git"
  baseBranch: "main"
  headBranch: "review/{{ .metadata.namespace }}-{{ .metadata.name }}"

  title: "📋 Configuration Review: {{ .metadata.name }}"
  body: |
    # Configuration Review Required

    A configuration change has been detected and requires review before deployment.

    ## Change Details
    - **Resource**: {{ .resourceRef.kind }}/{{ .resourceRef.name }}
    - **Namespace**: {{ .resourceRef.namespace }}
    - **Environment**: {{ .metadata.labels.environment }}

    ## Review Checklist
    - [ ] Configuration follows security guidelines
    - [ ] Resource limits are appropriate
    - [ ] No hardcoded secrets or credentials
    - [ ] Backwards compatibility maintained
    - [ ] Documentation updated (if needed)

    ## Deployment Plan
    1. Merge this PR to update the configuration repository
    2. CI/CD pipeline will validate the changes
    3. Automatic deployment to {{ .metadata.labels.environment }} environment
    4. Monitor deployment and rollback if needed

  labels: ["config-review", "{{ .metadata.labels.environment }}"]
  reviewers: ["config-team", "security-team"]
  draft: false

Multi-Environment Promotion

Promote configurations across environments:

apiVersion: gco.galos.one/v1
kind: PullRequest
metadata:
  name: env-promotion
spec:
  repository: "https://github.com/myorg/env-configs.git"
  baseBranch: "production"
  headBranch: "promote/staging-to-prod"

  title: "🚀 Promote {{ .metadata.name }} from staging to production"
  body: |
    # Environment Promotion

    This PR promotes configuration from **staging** to **production**.

    ## Staging Validation
    - [x] Configuration deployed successfully in staging
    - [x] All tests passing
    - [x] Performance benchmarks met
    - [x] Security scan completed

    ## Production Deployment Checklist
    - [ ] Deployment window scheduled
    - [ ] Monitoring alerts configured
    - [ ] Rollback plan prepared
    - [ ] Team notified

    ## Risk Assessment
    **Risk Level**: {{ .metadata.labels.risk | default "Medium" }}

    {{ if eq .metadata.labels.risk "High" }}
    ⚠️ **HIGH RISK CHANGE** - Additional approvals required
    {{ end }}

  conditions:
    - field: ".metadata.labels.environment"
      operator: "equals"
      value: "staging"
    - field: ".metadata.labels.validated"
      operator: "equals"
      value: "true"

Compliance and Audit Trail

Maintain compliance with automated documentation:

apiVersion: gco.galos.one/v1
kind: PullRequest
metadata:
  name: compliance-audit
spec:
  repository: "https://github.com/myorg/compliance-records.git"
  baseBranch: "main"
  headBranch: "audit/{{ .timestamp | date "2006-01" }}/{{ .metadata.name }}"

  title: "📊 Compliance Record: {{ .metadata.name }} - {{ .timestamp | date "January 2006" }}"
  body: |
    # Compliance Audit Record

    ## Change Summary
    - **Date**: {{ .timestamp | date "2006-01-02 15:04:05 UTC" }}
    - **Resource**: {{ .resourceRef.kind }}/{{ .resourceRef.name }}
    - **Namespace**: {{ .resourceRef.namespace }}
    - **Change Type**: {{ .changeType | default "Configuration Update" }}
    - **Initiated By**: {{ .initiator | default "System Automation" }}

    ## Compliance Verification
    - [x] Change follows organizational policies
    - [x] Appropriate approvals obtained
    - [x] Security implications assessed
    - [x] Data classification reviewed
    - [x] Retention policies applied

    ## Audit Trail
    | Field | Before | After |
    |-------|--------|-------|
    {{ range .changes }}
    | {{ .field }} | {{ .before }} | {{ .after }} |
    {{ end }}

    ## Supporting Documentation
    - Configuration Repository: [Link]({{ .configRepo }})
    - Change Request: {{ .changeRequestID | default "N/A" }}
    - Approval Workflow: [Link]({{ .approvalWorkflow }})

  resourceRef:
    path: "audit-records/{{ .timestamp | date "2006/01" }}/{{ .metadata.name }}.md"

  labels: ["compliance", "audit", "{{ .metadata.labels.classification }}"]
  assignees: ["compliance-team"]

Troubleshooting

Common Issues

Authentication Failures

# Check token permissions
kubectl get secret github-token -o yaml

# Test token manually
curl -H "Authorization: token $(echo '<base64-token>' | base64 -d)" \
     https://api.github.com/user

# Common errors:
# - Token expired
# - Insufficient permissions
# - Invalid repository URL

Branch Creation Failures

# Check PullRequest status
kubectl describe pullrequest mypr

# Common errors:
# - Branch already exists (check branchOptions.updateIfExists)
# - Base branch doesn't exist
# - Insufficient repository permissions

PR Creation Failures

# Check controller logs
kubectl logs -n git-change-operator-system -l control-plane=controller-manager

# Common errors:
# - API rate limits exceeded
# - Invalid PR template
# - Repository not found
# - Network connectivity issues

Debug Commands

# Check PullRequest resources
kubectl get pullrequest -A -o wide

# Get detailed status
kubectl get pullrequest mypr -o jsonpath='{.status}' | jq

# View events
kubectl get events --field-selector involvedObject.name=mypr

# Test resource references
kubectl get configmap app-config -o yaml

Best Practices

PR Management

  1. Use descriptive titles that clearly indicate the change
  2. Provide comprehensive descriptions with context and impact
  3. Include validation checklists for reviewers
  4. Link to related issues and documentation
  5. Set appropriate labels for filtering and automation

Branch Strategy

  1. Use meaningful branch names with consistent patterns
  2. Clean up branches after PR merge
  3. Protect important branches from direct pushes
  4. Use branch prefixes to categorize changes

Review Process

  1. Assign appropriate reviewers based on change impact
  2. Require approvals for sensitive changes
  3. Use draft PRs for work-in-progress changes
  4. Enable auto-merge for trusted automated changes
  5. Set up status checks to validate changes

Security

  1. Use minimal token permissions required for operations
  2. Rotate tokens regularly and monitor usage
  3. Audit PR creation and review processes
  4. Sanitize sensitive data before including in PRs
  5. Monitor for unauthorized changes

Next Steps

For enterprise PullRequest patterns, see our Corporate Setup Guide.