# CI/CD Pipeline Examples

Continuous integration and deployment workflows with Provisioning.

## GitHub Actions Workflow

Integrate Provisioning with GitHub Actions:

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

on:
  push:
    branches: [main]
    paths:
      - 'infrastructure/**'
      - '.github/workflows/deploy.yml'
  pull_request:
    branches: [main]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Set up Nushell
        run: |
          sudo apt-get update
          sudo apt-get install -y nushell cargo

      - name: Install Provisioning
        run: cargo install provisioning

      - name: Validate configuration
        run: |
          provisioning workspace use ci-environment
          provisioning infra validate

      - name: Check linting
        run: |
          provisioning lint config/**/*.ncl

  plan:
    needs: validate
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v3

      - name: Set up Provisioning
        run: cargo install provisioning

      - name: Plan deployment
        run: |
          provisioning workspace use ci-environment
          provisioning infra plan --output plan.json

      - name: Comment on PR
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('fs');
            const plan = JSON.parse(fs.readFileSync('plan.json'));
            const body = `## Infrastructure Plan

\`\`\`
${JSON.stringify(plan, null, 2)}
\`\`\``;
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: body
            });

  deploy:
    needs: [validate, plan]
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    environment:
      name: production
    steps:
      - uses: actions/checkout@v3

      - name: Set up Provisioning
        run: cargo install provisioning

      - name: Deploy infrastructure
        run: |
          provisioning workspace use prod-environment
          provisioning infra apply --auto-approve

      - name: Verify deployment
        run: |
          provisioning infra verify
          provisioning infra test --suite smoke-tests

      - name: Notify deployment
        if: always()
        run: |
          DEPLOYMENT_STATUS=${{ job.status }}
          curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
            -d "{\"text\": \"Infrastructure deployment $DEPLOYMENT_STATUS\"}"
```

## GitLab CI Pipeline

GitLab CI configuration for Provisioning:

```yaml
# .gitlab-ci.yml
stages:
  - validate
  - plan
  - deploy
  - verify

variables:
  PROVISIONING_WORKSPACE: "ci-environment"

before_script:
  - apt-get update && apt-get install -y nushell cargo
  - cargo install provisioning

validate:
  stage: validate
  script:
    - provisioning infra validate
    - provisioning lint config/**/*.ncl
  artifacts:
    reports:
      unit: validation-report.json

plan:
  stage: plan
  script:
    - provisioning infra plan --output plan.json
  artifacts:
    paths:
      - plan.json
  only:
    - merge_requests

plan_prod:
  stage: plan
  script:
    - provisioning workspace use prod-environment
    - provisioning infra plan --output plan-prod.json
  artifacts:
    paths:
      - plan-prod.json
  only:
    - main

deploy_staging:
  stage: deploy
  script:
    - provisioning workspace use staging-environment
    - provisioning infra apply --auto-approve
    - provisioning infra verify
  environment:
    name: staging
    url:  [https://staging.example.com](https://staging.example.com)
  only:
    - main

deploy_production:
  stage: deploy
  script:
    - provisioning workspace use prod-environment
    - provisioning infra apply --auto-approve
    - provisioning infra verify
  environment:
    name: production
    url:  [https://example.com](https://example.com)
  when: manual
  only:
    - main

verify:
  stage: verify
  script:
    - provisioning infra test --suite smoke-tests
    - provisioning infra test --suite integration-tests
  artifacts:
    reports:
      junit: test-results.xml
```

## Nushell Deployment Script

Custom deployment script with rollback:

```nushell
#!/usr/bin/env nu

# Configuration
let config = {
  workspace = "production",
  max_retries = 3,
  rollback_on_failure = true,
  notify = true
}

# Logging function
def log [level message] {
  let timestamp = (date now | format date "%Y-%m-%d %H:%M:%S")
  print $"[$timestamp] [$level] $message"
}

# Pre-deployment checks
def pre_flight_checks [] {
  log "INFO" "Running pre-flight checks..."

  try {
    # Check workspace exists
    provisioning workspace list | where name == $config.workspace | length | if $in > 0 { true } else { false }

    # Check configuration validity
    provisioning infra validate

    # Check all required secrets are available
    let required_secrets = ["db_password", "api_key", "encryption_key"]
    $required_secrets | each { | secret |
      provisioning vault get $secret | if ($in | is-empty) { error $"Missing secret: ($secret)" }
    }

    log "INFO" "Pre-flight checks passed"
    true
  } catch { | err |
    log "ERROR" $"Pre-flight checks failed: ($err)"
    false
  }
}

# Deploy with retry logic
def deploy_with_retry [attempt] {
  let max_attempts = $config.max_retries

  if $attempt > $max_attempts {
    error "Max deployment attempts reached"
  }

  log "INFO" $"Deployment attempt ($attempt) of ($max_attempts)..."

  try {
    provisioning workspace use $config.workspace
    provisioning infra apply --auto-approve

    log "INFO" "Deployment successful"
    true
  } catch { | err |
    log "WARN" $"Deployment attempt ($attempt) failed: ($err)"

    if $attempt < $max_attempts {
      log "INFO" "Retrying in 30 seconds..."
      sleep 30sec
      deploy_with_retry ($attempt + 1)
    } else {
      error "Deployment failed after $max_attempts attempts"
    }
  }
}

# Verification tests
def verify_deployment [] {
  log "INFO" "Verifying deployment..."

  try {
    # Run health checks
    provisioning infra test --suite health-checks

    # Run smoke tests
    provisioning infra test --suite smoke-tests

    # Run integration tests
    provisioning infra test --suite integration-tests

    log "INFO" "All verification tests passed"
    true
  } catch { | err |
    log "ERROR" $"Verification failed: ($err)"
    false
  }
}

# Rollback deployment
def rollback [] {
  log "WARN" "Rolling back deployment..."

  try {
    provisioning workspace use $config.workspace
    provisioning infra rollback --checkpoint latest

    log "INFO" "Rollback completed successfully"
  } catch { | err |
    log "ERROR" $"Rollback failed: ($err)"
    false
  }
}

# Notification
def notify [status message] {
  if $config.notify {
    let body = {
      status: $status,
      workspace: $config.workspace,
      message: $message,
      timestamp: (date now)
    }

    # Send to Slack or other service
    http post $env.WEBHOOK_URL --content-type "application/json" --data ($body | to json)
  }
}

# Main deployment flow
def main [] {
  log "INFO" "Starting deployment to ($config.workspace)..."

  # Pre-flight checks
  if not (pre_flight_checks) {
    notify "failed" "Pre-flight checks failed"
    exit 1
  }

  # Deploy with retries
  if not (deploy_with_retry 1) {
    if $config.rollback_on_failure {
      rollback
    }
    notify "failed" "Deployment failed"
    exit 1
  }

  # Verify deployment
  if not (verify_deployment) {
    if $config.rollback_on_failure {
      rollback
    }
    notify "failed" "Verification failed"
    exit 1
  }

  # Success
  log "INFO" "Deployment completed successfully"
  notify "success" "Deployment completed and verified"
}

main
```

## Multi-Environment Pipeline

Deploy to dev → staging → production:

```nickel
{
  batch = {
    name = "multi-environment-deployment",
    description = "Progressive deployment across environments",

    variables = {
      git_commit = "${GIT_COMMIT_SHA}",
      git_branch = "${GIT_BRANCH}",
      deployed_by = "${CI_USER}"
    },

    operations = [
      # Development
      {
        name = "deploy-dev",
        action = "deploy_infrastructure",
        workspace = "dev-environment",
        variables = {
          commit_sha = "${git_commit}",
          branch = "${git_branch}"
        }
      },
      {
        name = "test-dev",
        action = "run_tests",
        workspace = "dev-environment",
        test_suite = "unit-tests,integration-tests",
        depends_on = ["deploy-dev"]
      },

      # Staging (only if dev passes)
      {
        name = "deploy-staging",
        action = "deploy_infrastructure",
        workspace = "staging-environment",
        depends_on = ["test-dev"]
      },
      {
        name = "test-staging",
        action = "run_tests",
        workspace = "staging-environment",
        test_suite = "smoke-tests,performance-tests",
        depends_on = ["deploy-staging"]
      },

      # Production (manual approval)
      {
        name = "deploy-prod",
        action = "deploy_infrastructure",
        workspace = "prod-environment",
        depends_on = ["test-staging"],
        require_approval = true,
        approval_timeout = 3600  # 1 hour to approve
      },
      {
        name = "test-prod",
        action = "run_tests",
        workspace = "prod-environment",
        test_suite = "smoke-tests,production-tests",
        depends_on = ["deploy-prod"]
      },

      # Cleanup (optional)
      {
        name = "cleanup-dev-artifacts",
        action = "cleanup",
        workspace = "dev-environment",
        depends_on = ["test-prod"],
        retention = 7  # Keep for 7 days
      }
    ]
  }
}
```

## Container Image Building

Build and push container images as part of pipeline:

```nushell
#!/usr/bin/env nu

let config = {
  registry = "docker.io",
  organization = "myorg",
  base_image = "ubuntu:22.04"
}

# Build container image
def build-image [app_name version] {
  print $"Building image: ($app_name):($version)"

  let dockerfile_content = $"
FROM ($config.base_image)
RUN apt-get update && apt-get install -y nushell
COPY . /app
WORKDIR /app
RUN ./build.sh
ENTRYPOINT [\"nu\", \"main.nu\"]
"

  $dockerfile_content | save Dockerfile

  # Build image
  exec podman build -t $"($config.registry)/($config.organization)/($app_name):($version)" .
}

# Push to registry
def push-image [app_name version] {
  print $"Pushing image: ($app_name):($version)"

  let image = $"($config.registry)/($config.organization)/($app_name):($version)"

  # Login to registry
  echo $env.REGISTRY_PASSWORD | exec podman login -u $env.REGISTRY_USER --password-stdin $config.registry

  # Push image
  exec podman push $image

  # Also tag as 'latest'
  exec podman tag $image $"($config.registry)/($config.organization)/($app_name):latest"
  exec podman push $"($config.registry)/($config.organization)/($app_name):latest"

  print "Image pushed successfully"
}

# Main
let app_name = "my-app"
let version = (git describe --tags --always)

build-image $app_name $version
push-image $app_name $version
```

## Release Management

Tagged release pipeline:

```bash
#!/usr/bin/env nu

# When a release tag is pushed (v1.2.3)
def on-release-tag [tag] {
  print $"Processing release: ($tag)"

  let version = ($tag | str replace 'v' '')

  # 1. Build and push container images
  ./scripts/build-images.nu $version

  # 2. Update documentation version
  sed -i "s/version = .*/version = \"$version\"/" docs/book.toml

  # 3. Generate changelog
  git log $(git describe --tags --abbrev=0)..HEAD --oneline > CHANGELOG-$version.md

  # 4. Create release notes
  gh release create $tag \
    --title "Release $version" \
    --notes-file CHANGELOG-$version.md

  # 5. Deploy to production
  provisioning workspace use prod-environment
  provisioning batch submit workflows/production-deployment.ncl \
    --variables "version=$version,release_tag=$tag"

  # 6. Post-deployment verification
  provisioning infra test --suite production-tests
}
```

## Related Documentation

- [Batch Workflow System](../infrastructure/batch-workflows.md)
- [Testing Guide](../development/testing.md)
- [Orchestrator](../features/orchestrator.md)
- [Workspace Management](./workspace-examples.md)
