LogoContainerPub

CI/CD Integration

Integrate dart_cloud_deploy with CI/CD pipelines for automated deployments

CI/CD Integration#

This guide covers integrating the Backend Deploy CLI with various CI/CD platforms for automated deployments.

Overview#

The dart_cloud_deploy CLI is designed to work seamlessly in CI/CD environments:

  • Non-interactive mode - All options can be passed via command line
  • Exit codes - Proper exit codes for pipeline status
  • Secrets handling - Support for environment variables and secret managers
  • Dry run - Preview deployments before executing

GitHub Actions#

Basic Deployment Workflow#

name: Deploy Backend

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Dart
        uses: dart-lang/setup-dart@v1
        with:
          sdk: "3.10.4"

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install Deploy CLI
        run: |
          cd tools/dart_packages/dart_cloud_deploy_cli
          dart pub get
          dart pub global activate --source path .

      - name: Initialize Environment
        run: dart_cloud_deploy init

      - name: Setup SSH Key
        run: |
          mkdir -p ~/.ssh
          echo "$" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H $ >> ~/.ssh/known_hosts

      - name: Create Config
        run: |
          cat > deploy.yaml << EOF
          name: dart_cloud_backend
          environment: production
          project_path: .
          env_file_path: .env

          container:
            runtime: podman
            compose_file: docker-compose.yml
            project_name: dart_cloud
            services:
              backend: dart_cloud_backend
              postgres: dart_cloud_postgres

          host:
            host: $
            port: 22
            user: $
            ssh_key_path: ~/.ssh/id_rsa

          ansible:
            extra_vars:
              app_dir: /opt/dart_cloud
          EOF

      - name: Create .env file
        run: |
          cat > .env << EOF
          DATABASE_URL=$
          JWT_SECRET=$
          API_KEY=$
          EOF

      - name: Deploy
        run: dart_cloud_deploy deploy-dev --skip-secrets

    environment:
      name: production
      url: https://api.example.com

Multi-Environment Workflow#

name: Deploy

on:
  push:
    branches:
      - main
      - develop
  workflow_dispatch:
    inputs:
      environment:
        description: "Environment to deploy"
        required: true
        default: "dev"
        type: choice
        options:
          - dev
          - staging
          - production

jobs:
  deploy:
    runs-on: ubuntu-latest

    env:
      ENVIRONMENT: $

    steps:
      - uses: actions/checkout@v4

      - uses: dart-lang/setup-dart@v1

      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install CLI
        run: |
          cd tools/dart_packages/dart_cloud_deploy_cli
          dart pub get
          dart pub global activate --source path .

      - name: Initialize
        run: dart_cloud_deploy init

      - name: Setup SSH
        run: |
          mkdir -p ~/.ssh
          echo "$" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa

      - name: Deploy to Dev
        if: env.ENVIRONMENT == 'dev'
        run: dart_cloud_deploy deploy-dev -c deploy-dev.yaml --skip-secrets
        env:
          SERVER_HOST: $

      - name: Deploy to Staging
        if: env.ENVIRONMENT == 'staging'
        run: dart_cloud_deploy deploy-dev -c deploy-staging.yaml --skip-secrets
        env:
          SERVER_HOST: $

      - name: Deploy to Production
        if: env.ENVIRONMENT == 'production'
        run: dart_cloud_deploy deploy-dev -c deploy-prod.yaml --skip-secrets
        env:
          SERVER_HOST: $

Deployment with Approval#

name: Production Deploy

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build and test
        run: |
          dart pub get
          dart test

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://api.example.com

    steps:
      - uses: actions/checkout@v4

      - uses: dart-lang/setup-dart@v1

      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install and Deploy
        run: |
          cd tools/dart_packages/dart_cloud_deploy_cli
          dart pub get
          dart pub global activate --source path .
          dart_cloud_deploy init
          dart_cloud_deploy deploy-dev -c deploy-prod.yaml --skip-secrets

GitLab CI/CD#

Basic Pipeline#

stages:
  - build
  - deploy

variables:
  DART_VERSION: "3.10.4"

build:
  stage: build
  image: dart:$DART_VERSION
  script:
    - dart pub get
    - dart test
  only:
    - main
    - develop

deploy_dev:
  stage: deploy
  image: dart:$DART_VERSION
  before_script:
    - apt-get update && apt-get install -y python3 python3-venv openssh-client
    - cd tools/dart_packages/dart_cloud_deploy_cli
    - dart pub get
    - dart pub global activate --source path .
    - export PATH="$PATH:$HOME/.pub-cache/bin"
  script:
    - dart_cloud_deploy init
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts
    - dart_cloud_deploy deploy-dev -c deploy-dev.yaml --skip-secrets
  environment:
    name: development
    url: https://dev-api.example.com
  only:
    - develop

deploy_prod:
  stage: deploy
  image: dart:$DART_VERSION
  before_script:
    - apt-get update && apt-get install -y python3 python3-venv openssh-client
    - cd tools/dart_packages/dart_cloud_deploy_cli
    - dart pub get
    - dart pub global activate --source path .
    - export PATH="$PATH:$HOME/.pub-cache/bin"
  script:
    - dart_cloud_deploy init
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts
    - dart_cloud_deploy deploy-dev -c deploy-prod.yaml --skip-secrets
  environment:
    name: production
    url: https://api.example.com
  only:
    - main
  when: manual

Jenkins#

Jenkinsfile#

pipeline {
    agent any

    environment {
        DART_HOME = tool 'dart-sdk'
        PATH = "${DART_HOME}/bin:${env.PATH}"
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Setup') {
            steps {
                sh '''
                    cd tools/dart_packages/dart_cloud_deploy_cli
                    dart pub get
                    dart pub global activate --source path .
                '''
            }
        }

        stage('Initialize') {
            steps {
                sh 'dart_cloud_deploy init'
            }
        }

        stage('Deploy to Dev') {
            when {
                branch 'develop'
            }
            steps {
                withCredentials([
                    sshUserPrivateKey(credentialsId: 'deploy-ssh-key', keyFileVariable: 'SSH_KEY'),
                    string(credentialsId: 'dev-server-host', variable: 'SERVER_HOST')
                ]) {
                    sh '''
                        mkdir -p ~/.ssh
                        cp $SSH_KEY ~/.ssh/id_rsa
                        chmod 600 ~/.ssh/id_rsa
                        ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts
                        dart_cloud_deploy deploy-dev -c deploy-dev.yaml --skip-secrets
                    '''
                }
            }
        }

        stage('Deploy to Production') {
            when {
                branch 'main'
            }
            input {
                message "Deploy to production?"
                ok "Deploy"
            }
            steps {
                withCredentials([
                    sshUserPrivateKey(credentialsId: 'deploy-ssh-key', keyFileVariable: 'SSH_KEY'),
                    string(credentialsId: 'prod-server-host', variable: 'SERVER_HOST')
                ]) {
                    sh '''
                        mkdir -p ~/.ssh
                        cp $SSH_KEY ~/.ssh/id_rsa
                        chmod 600 ~/.ssh/id_rsa
                        ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts
                        dart_cloud_deploy deploy-dev -c deploy-prod.yaml --skip-secrets
                    '''
                }
            }
        }
    }

    post {
        always {
            cleanWs()
        }
    }
}

CircleCI#

config.yml#

version: 2.1

orbs:
  dart: circleci/dart@2.0

jobs:
  build:
    docker:
      - image: dart:3.10.4
    steps:
      - checkout
      - run:
          name: Install dependencies
          command: dart pub get
      - run:
          name: Run tests
          command: dart test

  deploy:
    docker:
      - image: dart:3.10.4
    parameters:
      environment:
        type: string
        default: "dev"
    steps:
      - checkout
      - run:
          name: Install system dependencies
          command: apt-get update && apt-get install -y python3 python3-venv openssh-client
      - run:
          name: Install Deploy CLI
          command: |
            cd tools/dart_packages/dart_cloud_deploy_cli
            dart pub get
            dart pub global activate --source path .
      - run:
          name: Initialize
          command: dart_cloud_deploy init
      - run:
          name: Setup SSH
          command: |
            mkdir -p ~/.ssh
            echo "$SSH_PRIVATE_KEY" | base64 -d > ~/.ssh/id_rsa
            chmod 600 ~/.ssh/id_rsa
            ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts
      - run:
          name: Deploy
          command: dart_cloud_deploy deploy-dev -c deploy-<< parameters.environment >>.yaml --skip-secrets

workflows:
  build-and-deploy:
    jobs:
      - build
      - deploy:
          name: deploy-dev
          environment: dev
          requires:
            - build
          filters:
            branches:
              only: develop
      - deploy:
          name: deploy-prod
          environment: prod
          requires:
            - build
          filters:
            branches:
              only: main

Best Practices#

1. Use Environment-Specific Configs#

Create separate configuration files for each environment:

deploy-local.yaml
deploy-dev.yaml
deploy-staging.yaml
deploy-prod.yaml

2. Store Secrets Securely#

Never commit secrets to version control. Use:

  • GitHub Actions: Repository secrets
  • GitLab CI: CI/CD variables (masked)
  • Jenkins: Credentials plugin
  • CircleCI: Environment variables

3. Use Dry Run for Testing#

Always test with --dry-run first:

- name: Dry Run
  run: dart_cloud_deploy deploy-dev --dry-run

- name: Deploy
  run: dart_cloud_deploy deploy-dev

4. Add Health Checks#

Verify deployment success:

- name: Deploy
  run: dart_cloud_deploy deploy-dev

- name: Health Check
  run: |
    sleep 30
    curl -f https://api.example.com/health || exit 1

5. Use Deployment Environments#

Configure environments for approval workflows:

environment:
  name: production
  url: https://api.example.com

6. Cache Dependencies#

Speed up pipelines by caching:

- name: Cache Dart packages
  uses: actions/cache@v3
  with:
    path: ~/.pub-cache
    key: $-pub-$

- name: Cache Python venv
  uses: actions/cache@v3
  with:
    path: .venv
    key: $-venv-$

7. Rollback Strategy#

Implement rollback capability:

- name: Deploy
  id: deploy
  run: dart_cloud_deploy deploy-dev
  continue-on-error: true

- name: Rollback on Failure
  if: steps.deploy.outcome == 'failure'
  run: |
    echo "Deployment failed, rolling back..."
    dart_cloud_deploy deploy-dev -e app_version=$

Secrets Management#

Using OpenBao in CI/CD#

If you have OpenBao/Vault available in your CI environment:

- name: Setup OpenBao Token
  run: |
    mkdir -p ~/.openbao
    echo "$" > ~/.openbao/token

- name: Deploy with Secrets
  run: dart_cloud_deploy deploy-dev # Will fetch secrets automatically

Manual .env Generation#

For environments without OpenBao:

- name: Create .env
  run: |
    cat > .env << EOF
    DATABASE_URL=$
    JWT_SECRET=$
    REDIS_URL=$
    EOF

- name: Deploy
  run: dart_cloud_deploy deploy-dev --skip-secrets

Troubleshooting CI/CD#

SSH Connection Issues#

- name: Debug SSH
  run: |
    ssh -vvv -i ~/.ssh/id_rsa $USER@$HOST echo "Connection successful"

Ansible Verbose Output#

- name: Deploy with Debug
  run: dart_cloud_deploy deploy-dev -v

Check Exit Codes#

- name: Deploy
  run: |
    dart_cloud_deploy deploy-dev
    echo "Exit code: $?"

Next Steps#