# GitOps Infrastructure Deployment Guide

Implement infrastructure-as-code deployment using Git as source of truth with automated reconciliation.

## GitOps Architecture

```text
GitHub Repository (Source of Truth)
    ↓
    commit push
    ↓
GitHub Actions Workflow
    ├─ Syntax validation
    ├─ Schema validation
    ├─ Cost estimation
    ├─ Security scanning
    └─ Approval required
    ↓
    approved
    ↓
Provisioning Operator (Running in cluster)
    ├─ Watches Git repository
    ├─ Detects configuration changes
    ├─ Applies configurations automatically
    ├─ Reports status back to Git
    └─ Rolls back on failure
    ↓
Infrastructure (AWS, UpCloud, On-Premise)
```

## Repository Structure

```text
provisioning-gitops/
├── .github/workflows/
│   ├── validate.yml
│   ├── test.yml
│   ├── deploy.yml
│   └── rollback.yml
├── workspaces/
│   ├── production/
│   │   ├── config/
│   │   │   ├── main.ncl
│   │   │   ├── compute.ncl
│   │   │   ├── database.ncl
│   │   │   └── networking.ncl
│   │   ├── kustomization.yaml
│   │   └── README.md
│   ├── staging/
│   │   └── config/
│   └── development/
│       └── config/
├── kustomize/
│   ├── base/
│   │   ├── namespace.yaml
│   │   └── provisioning-operator.yaml
│   └── overlays/
│       ├── production/
│       ├── staging/
│       └── development/
└── README.md
```

## Automated Validation Workflow

```yaml
# .github/workflows/validate.yml
name: Validate Infrastructure Changes

on:
  pull_request:
    paths:
      - 'workspaces/**'
      - '.github/workflows/**'

jobs:
  validate:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Install provisioning CLI
        run: curl -fsSL  [https://provisioning.io/install.sh](https://provisioning.io/install.sh) | sh

      - name: Syntax validation
        run: |
          for file in $(find workspaces -name "*.ncl"); do
            provisioning validate syntax --file "$file"
          done

      - name: Schema validation
        run: |
          provisioning validate schema \
            --config workspaces/production/config/main.ncl \
            --schemas provisioning/schemas/*.ncl

      - name: Policy validation
        run: |
          provisioning validate policies \
            --config workspaces/production/config/main.ncl \
            --policies-file .provisioning-policies.yaml

      - name: Cost estimation
        run: |
          provisioning estimate cost \
            --config workspaces/production/config/main.ncl \
            --output cost-estimate.json

      - name: Security scan
        run: |
          provisioning scan security \
            --config workspaces/production/config/main.ncl \
            --output security-scan.json

      - name: Comment PR with results
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const costEstimate = JSON.parse(fs.readFileSync('cost-estimate.json'));
            const securityScan = JSON.parse(fs.readFileSync('security-scan.json'));

            let comment = '## Infrastructure Change Review

';
            comment += `**Estimated Cost**: $${costEstimate.monthly_cost}/month
`;
            comment += `**Security Issues**: ${securityScan.issues.length}
`;

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: comment
            });
```

## Deployment Workflow

```yaml
# .github/workflows/deploy.yml
name: Deploy Infrastructure

on:
  pull_request:
    types: [closed]

jobs:
  deploy:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Determine workspace
        id: workspace
        run: |
          # Extract workspace from branch name (e.g., deploy/production)
          WORKSPACE=$(echo ${{ github.head_ref }} | cut -d'/' -f2)
          echo "workspace=$WORKSPACE" >> $GITHUB_OUTPUT

      - name: Plan infrastructure changes
        run: |
          provisioning infra plan \
            --config workspaces/${{ steps.workspace.outputs.workspace }}/config/main.ncl \
            --output plan.json

      - name: Request approval (for production)
        if: steps.workspace.outputs.workspace == 'production'
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '🔒 **Production Deployment**: Awaiting manual approval from infrastructure team'
            });

      - name: Apply infrastructure changes
        run: |
          provisioning infra apply \
            --config workspaces/${{ steps.workspace.outputs.workspace }}/config/main.ncl \
            --auto-approve

      - name: Run smoke tests
        run: |
          provisioning test smoke \
            --workspace ${{ steps.workspace.outputs.workspace }}

      - name: Comment deployment status
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '✅ **Deployment Complete**: Infrastructure changes applied successfully'
            });
```

## Rollback Workflow

```yaml
# .github/workflows/rollback.yml
name: Rollback Infrastructure

on:
  workflow_dispatch:
    inputs:
      workspace:
        description: 'Workspace to rollback'
        required: true
        type: choice
        options:
          - production
          - staging
          - development
      commit:
        description: 'Commit to rollback to'
        required: true

jobs:
  rollback:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Checkout target commit
        run: |
          git fetch origin
          git checkout ${{ github.event.inputs.commit }}

      - name: Plan rollback
        run: |
          provisioning infra plan \
            --config workspaces/${{ github.event.inputs.workspace }}/config/main.ncl \
            --output rollback-plan.json

      - name: Request approval
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: `Rollback Requested: ${{ github.event.inputs.workspace }}`,
              body: `**Workspace**: ${{ github.event.inputs.workspace }}
` +
                    `**Target Commit**: ${{ github.event.inputs.commit }}

` +
                    `Approve this issue to proceed with rollback.`,
              labels: ['rollback', 'requires-approval']
            });

      - name: Apply rollback
        run: |
          provisioning infra apply \
            --config workspaces/${{ github.event.inputs.workspace }}/config/main.ncl \
            --auto-approve

      - name: Verify rollback
        run: |
          provisioning test smoke \
            --workspace ${{ github.event.inputs.workspace }}
```

## Provisioning Operator

Deploy operator to watch Git repository and reconcile infrastructure:

```yaml
# kustomize/base/provisioning-operator.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: provisioning-system

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: provisioning-operator
  namespace: provisioning-system

spec:
  replicas: 1
  selector:
    matchLabels:
      app: provisioning-operator

  template:
    metadata:
      labels:
        app: provisioning-operator

    spec:
      serviceAccountName: provisioning-operator

      containers:
      - name: operator
        image: provisioning:latest
        imagePullPolicy: Always

        env:
        - name: GIT_REPOSITORY_URL
          value:  [https://github.com/org/provisioning-gitops.git](https://github.com/org/provisioning-gitops.git)

        - name: GIT_REPOSITORY_BRANCH
          value: main

        - name: GIT_SYNC_INTERVAL
          value: "60"  # seconds

        - name: PROVISIONING_ENVIRONMENT
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName

        volumeMounts:
        - name: config
          mountPath: /etc/provisioning

        - name: git-repo
          mountPath: /var/provisioning/git

        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: 2000m
            memory: 2Gi

      volumes:
      - name: config
        configMap:
          name: provisioning-operator-config

      - name: git-repo
        emptyDir: {}

---

apiVersion: v1
kind: ServiceAccount
metadata:
  name: provisioning-operator
  namespace: provisioning-system

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: provisioning-operator

rules:
- apiGroups: ["provisioning.io"]
  resources: ["infrastructures"]
  verbs: ["get", "list", "watch"]

- apiGroups: ["provisioning.io"]
  resources: ["infrastructures/status"]
  verbs: ["update", "patch"]

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: provisioning-operator

roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: provisioning-operator

subjects:
- kind: ServiceAccount
  name: provisioning-operator
  namespace: provisioning-system
```

## Multi-Environment Management

```nushell
def gitops-sync [--environment: string] {
    print $"Syncing ($environment) infrastructure from Git..."

    # Get latest from Git
    git pull origin main

    # Get current configuration hash
    let config_hash = (
        cd workspaces/$environment &&
        find config -name "*.ncl" | xargs cat | sha256sum | cut -d' ' -f1
    )

    # Get last applied hash
    let last_applied = (
        provisioning infra status \
            --workspace $environment \
            --field config_hash
    )

    # Only apply if configuration changed
    if $config_hash != $last_applied {
        print "Configuration changed, applying updates..."

        let result = (
            provisioning infra apply \
                --config workspaces/$environment/config/main.ncl \
                --auto-approve
        )

        if $result.status == "success" {
            print $"✓ ($environment) infrastructure synchronized"
        } else {
            print $"✗ ($environment) synchronization failed: ($result.error)"
            return 1
        }
    } else {
        print $"✓ ($environment) infrastructure already synchronized"
    }
}
```

## Status Reporting

```nushell
def gitops-status [] {
    print "GitOps Infrastructure Status"
    print "━━━━━━━━━━━━━━━━━━━━━━━━━━━"

    let workspaces = ["production", "staging", "development"]

    $workspaces | each { | workspace |
        let status = (
            provisioning infra status --workspace $workspace
        )

        let git_status = (
            cd workspaces/$workspace &&
            git status --porcelain | length
        )

        print ""
        print $"Workspace: ($workspace)"
        print $"  Infrastructure Status: ($status.status)"
        print $"  Last Sync: ($status.last_sync)"
        print $"  Config Hash: ($status.config_hash)"
        print $"  Git Changes Pending: ($git_status)"

        if $git_status > 0 {
            print "  ⚠️  Uncommitted changes in Git"
        }
    }
}
```

## Drift Detection

```nushell
def gitops-detect-drift [] {
    print "Detecting infrastructure drift from Git configuration..."

    let workspaces = ["production", "staging"]

    $workspaces | each { | workspace |
        let drift = (
            provisioning infra drift \
                --workspace $workspace \
                --from-git workspaces/$workspace/config/main.ncl
        )

        if ($drift.changes | length) > 0 {
            print ""
            print $"⚠️  Drift detected in ($workspace):"
            $drift.changes | each { | change |
                print $"  - ($change.resource): ($change.difference)"
            }
        } else {
            print $"✓ ($workspace): No drift detected"
        }
    }
}
```

## Best Practices

1. **Single Source of Truth**: Git repository is authoritative
2. **Immutable Commits**: Don't force-push or rewrite history
3. **Protected Branches**: Require approvals for production
4. **Automated Testing**: All changes validated before merge
5. **Audit Trail**: All changes tracked in Git history
6. **Drift Detection**: Regular checks for configuration drift
7. **Rollback Plan**: Keep previous commits available
8. **Secrets Management**: Use SecretumVault, never commit secrets

## See Also

- [CI/CD Integration Guide](../guides/ci-cd-integration.md)
- [Configuration System](../infrastructure/configuration-system.md)
- [Batch Workflow System](../infrastructure/batch-workflows.md)
