Automation and CI/CD

This guide provides practical examples for integrating Teenode with CI/CD systems, automating deployments, and building production pipelines.

GitHub Actions Workflows

Basic Deployment Workflow

Simple Git-based deployment on push to main branch:

name: Deploy to Teenode

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

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

      - name: Set up Teenode CLI
        run: |
          npm install -g teenode-cli

      - name: Authenticate with Teenode
        run: |
          teenode auth login --token ${{ secrets.TEENODE_API_TOKEN }}

      - name: Deploy application
        run: |
          teenode deployment create my-app \
            --branch main \
            --wait

      - name: Verify deployment
        run: |
          teenode deployment list my-app --limit 1 | grep "RUNNING"

      - name: Health check
        run: |
          curl -f https://my-app.teenode.app/health || exit 1

      - name: Notify Slack
        if: success()
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Deployment successful",
              "blocks": [{
                "type": "section",
                "text": {
                  "type": "mrkdwn",
                  "text": "Deployed: my-app"
                }
              }]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Advanced Multi-Environment Workflow

name: Multi-Environment Deployment

on:
  push:
    branches: [main, staging, develop]

env:
  TEENODE_TOKEN: ${{ secrets.TEENODE_API_TOKEN }}

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image_tag: ${{ steps.build.outputs.image_tag }}

    steps:
      - uses: actions/checkout@v3

      - name: Set up Docker
        uses: docker/setup-buildx-action@v2

      - name: Build image
        id: build
        run: |
          IMAGE_TAG="${{ github.event.repository.name }}:${{ github.sha }}"
          docker build -t $IMAGE_TAG .
          echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT

  deploy:
    needs: build
    runs-on: ubuntu-latest
    strategy:
      matrix:
        environment:
          - {name: 'develop', branch: 'develop', project: 'my-app-dev'}
          - {name: 'staging', branch: 'staging', project: 'my-app-staging'}
          - {name: 'production', branch: 'main', project: 'my-app-prod'}

    steps:
      - uses: actions/checkout@v3

      - name: Check branch match
        run: |
          if [ "${{ github.ref }}" != "refs/heads/${{ matrix.environment.branch }}" ]; then
            echo "Branch mismatch, skipping"
            exit 1
          fi

      - name: Install Teenode CLI
        run: npm install -g teenode-cli

      - name: Authenticate
        run: teenode auth login --token $TEENODE_TOKEN

      - name: Deploy to ${{ matrix.environment.name }}
        run: |
          teenode deployment create ${{ matrix.environment.project }} \
            --branch ${{ matrix.environment.branch }} \
            --wait \
            --timeout 600

      - name: Run smoke tests
        run: |
          URL="https://${{ matrix.environment.project }}.teenode.app"

          # Health check
          curl -f $URL/health || exit 1

          # API check
          curl -f $URL/api/status || exit 1

          # Database connectivity
          curl -f $URL/api/db-check || exit 1

      - name: Generate deployment report
        run: |
          teenode deployment describe \
            $(teenode deployment list ${{ matrix.environment.project }} --limit 1 | awk '{print $1}') \
            > deployment-report.json

      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: deployment-report-${{ matrix.environment.name }}
          path: deployment-report.json

      - name: Notify Slack on failure
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Deployment failed to ${{ matrix.environment.name }}",
              "attachments": [{
                "color": "danger",
                "text": "Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
              }]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Workflow with Attestation Verification

name: Deploy with Attestation Verification

on:
  push:
    branches: [main]

jobs:
  deploy-and-verify:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Install Teenode CLI
        run: npm install -g teenode-cli

      - name: Authenticate
        run: teenode auth login --token ${{ secrets.TEENODE_API_TOKEN }}

      - name: Deploy
        run: teenode deployment create my-app --branch main --wait

      - name: Verify Attestation
        run: |
          VM_NAME="my-app"

          # Get attestation
          teenode vm attest $VM_NAME --verify

          if [ $? -ne 0 ]; then
            echo "Attestation verification failed!"
            exit 1
          fi

          # Save attestation report
          teenode vm attest $VM_NAME --output attestation.json

      - name: Check Security Policy
        run: |
          # Ensure debug mode is disabled
          DEBUG_ALLOWED=$(jq '.report.policy.debug_allowed' attestation.json)

          if [ "$DEBUG_ALLOWED" = "true" ]; then
            echo "Security violation: debug mode enabled"
            exit 1
          fi

          # Check TCB version
          SNP_VERSION=$(jq '.report.current_tcb.snp' attestation.json)
          if [ "$SNP_VERSION" -lt 3 ]; then
            echo "Warning: SNP version $SNP_VERSION is outdated"
          fi

      - name: Upload attestation
        uses: actions/upload-artifact@v3
        with:
          name: attestation-report
          path: attestation.json

      - name: Approve deployment
        run: |
          echo "✓ Deployment verified and secure"
          exit 0

GitLab CI/CD Pipelines

Basic Pipeline

stages:
  - build
  - deploy
  - verify

variables:
  TEENODE_CLI_VERSION: latest

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - main

deploy:
  stage: deploy
  image: node:18-alpine
  script:
    - npm install -g teenode-cli
    - teenode auth login --token $TEENODE_API_TOKEN
    - teenode deployment create my-app --branch main --wait
  only:
    - main

verify:
  stage: verify
  image: node:18-alpine
  script:
    - npm install -g teenode-cli
    - teenode auth login --token $TEENODE_API_TOKEN
    - teenode deployment list my-app --limit 1
    - curl -f https://my-app.teenode.app/health || exit 1
  only:
    - main

Advanced Multi-Stage Pipeline

stages:
  - lint
  - build
  - test
  - deploy
  - monitor

lint:
  stage: lint
  image: node:18-alpine
  script:
    - npm ci
    - npm run lint
  cache:
    paths:
      - node_modules/

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build
        --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
        --build-arg VCS_REF=$CI_COMMIT_SHA
        -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        -t $CI_REGISTRY_IMAGE:latest
        .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main

test:
  stage: test
  image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  script:
    - npm ci
    - npm run test:unit
    - npm run test:integration
  artifacts:
    reports:
      junit: test-results.xml
    paths:
      - coverage/
    expire_in: 30 days

deploy_staging:
  stage: deploy
  image: node:18-alpine
  environment:
    name: staging
    url: https://staging.my-app.teenode.app
  script:
    - npm install -g teenode-cli
    - teenode auth login --token $TEENODE_API_TOKEN
    - teenode deployment create my-app-staging --branch main --wait
    - sleep 30
    - |
      HEALTH=$(curl -s -o /dev/null -w "%{http_code}" https://staging.my-app.teenode.app/health)
      if [ "$HEALTH" != "200" ]; then
        echo "Health check failed: HTTP $HEALTH"
        exit 1
      fi
  only:
    - main

deploy_production:
  stage: deploy
  image: node:18-alpine
  environment:
    name: production
    url: https://my-app.teenode.app
  script:
    - npm install -g teenode-cli
    - teenode auth login --token $TEENODE_API_TOKEN
    - teenode deployment create my-app --branch main --wait
    - sleep 30
    - |
      HEALTH=$(curl -s -o /dev/null -w "%{http_code}" https://my-app.teenode.app/health)
      if [ "$HEALTH" != "200" ]; then
        echo "Health check failed: HTTP $HEALTH"
        exit 1
      fi
  when: manual  # Require manual approval
  only:
    - main

monitor:
  stage: monitor
  image: node:18-alpine
  script:
    - npm install -g teenode-cli
    - teenode auth login --token $TEENODE_API_TOKEN
    - |
      # Check application metrics
      teenode project metrics my-app | tee metrics.json

      # Verify attestation
      teenode vm attest my-app --verify
  only:
    - main
  when: on_success

Bitbucket Pipelines

image: node:18-alpine

definitions:
  caches:
    npm: ~/.npm
    docker: ~/.docker

pipelines:
  branches:
    main:
      - step:
          name: Install Teenode CLI
          caches:
            - npm
          script:
            - npm install -g teenode-cli

      - step:
          name: Deploy to Teenode
          script:
            - teenode auth login --token $TEENODE_API_TOKEN
            - teenode deployment create my-app --branch main --wait
            - sleep 30
            - curl -f https://my-app.teenode.app/health || exit 1

      - step:
          name: Verify
          trigger: manual
          script:
            - teenode auth login --token $TEENODE_API_TOKEN
            - teenode deployment list my-app --limit 5
            - teenode project metrics my-app

Jenkins Pipeline

pipeline {
    agent any

    environment {
        TEENODE_TOKEN = credentials('teenode-api-token')
        APP_NAME = 'my-app'
        DOCKER_REGISTRY = 'docker.io'
        DOCKER_IMAGE = "\$DOCKER_REGISTRY/\$APP_NAME"
    }

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

        stage('Build') {
            steps {
                script {
                    sh '''
                        docker build -t \$DOCKER_IMAGE:\$BUILD_NUMBER .
                        docker tag \$DOCKER_IMAGE:\$BUILD_NUMBER \$DOCKER_IMAGE:latest
                    '''
                }
            }
        }

        stage('Test') {
            steps {
                script {
                    sh '''
                        npm ci
                        npm run test:unit
                        npm run test:integration
                    '''
                }
            }
        }

        stage('Deploy Staging') {
            steps {
                script {
                    sh '''
                        npm install -g teenode-cli
                        teenode auth login --token \$TEENODE_TOKEN
                        teenode deployment create \$APP_NAME-staging \
                          --branch main \
                          --wait
                    '''
                }
            }
        }

        stage('Smoke Test') {
            steps {
                script {
                    sh '''
                        sleep 30
                        curl -f https://staging.\$APP_NAME.teenode.app/health || exit 1
                        curl -f https://staging.\$APP_NAME.teenode.app/api/status || exit 1
                    '''
                }
            }
        }

        stage('Deploy Production') {
            when {
                branch 'main'
                tag "v*"
            }
            steps {
                input 'Deploy to production?'
                script {
                    sh '''
                        teenode auth login --token \$TEENODE_TOKEN
                        teenode deployment create \$APP_NAME \
                          --branch main \
                          --wait
                    '''
                }
            }
        }

        stage('Verify Production') {
            steps {
                script {
                    sh '''
                        sleep 30
                        curl -f https://\$APP_NAME.teenode.app/health || exit 1

                        # Verify attestation
                        teenode auth login --token \$TEENODE_TOKEN
                        teenode vm attest \$APP_NAME --verify
                    '''
                }
            }
        }
    }

    post {
        always {
            junit 'test-results.xml'
            archiveArtifacts artifacts: 'logs/**', allowEmptyArchive: true
        }
        failure {
            emailext(
                subject: "Build failed: \$JOB_NAME \$BUILD_NUMBER",
                body: "Build \$BUILD_URL failed",
                to: "\$CHANGE_AUTHOR_EMAIL"
            )
        }
        success {
            slackSend(
                color: 'good',
                message: "Deployment successful: \$APP_NAME to production"
            )
        }
    }
}

Infrastructure as Code

Terraform Integration

terraform {
  required_providers {
    teenode = {
      source  = "teenode/teenode"
      version = "~> 1.0"
    }
  }
}

provider "teenode" {
  api_token = var.teenode_api_token
  api_url   = "https://api.teenode.com"
}

variable "teenode_api_token" {
  sensitive = true
  type      = string
}

variable "environment" {
  type    = string
  default = "production"
}

# Deploy application
resource "teenode_project" "app" {
  name   = "my-app-${var.environment}"
  type   = "GIT_DEPLOY"
  git_url = "https://github.com/myorg/my-app"
  branch = "main"
  region = "us-east"

  environment_variables = {
    ENVIRONMENT    = var.environment
    LOG_LEVEL      = var.environment == "production" ? "info" : "debug"
    DATABASE_URL   = var.database_url
  }

  auto_deploy_enabled = true
  auto_deploy_branch  = "main"

  tags = {
    environment = var.environment
    managed_by  = "terraform"
  }
}

# Create VMs for app
resource "teenode_vm" "app_vm" {
  count = var.environment == "production" ? 3 : 1

  name        = "app-vm-${count.index + 1}"
  cpu         = var.environment == "production" ? 8 : 2
  memory      = var.environment == "production" ? 16384 : 4096
  region      = "us-east"
  ssh_key_ids = [teenode_ssh_key.main.id]

  tags = {
    environment = var.environment
    role        = "app-server"
  }
}

# SSH key for access
resource "teenode_ssh_key" "main" {
  name       = "terraform-${var.environment}"
  public_key = file("~/.ssh/id_rsa.pub")
}

# Outputs
output "project_name" {
  value = teenode_project.app.name
}

output "vm_ips" {
  value = teenode_vm.app_vm[*].ip_address
}

output "app_url" {
  value = "https://${teenode_project.app.name}.teenode.app"
}

Ansible Playbook

---
- name: Deploy Teenode Application
  hosts: localhost
  gather_facts: yes
  vars:
    app_name: my-app
    environment: production
    region: us-east

  tasks:
    - name: Install Teenode CLI
      npm:
        name: teenode-cli
        global: yes

    - name: Authenticate with Teenode
      shell: |
        teenode auth login --token {{ teenode_api_token }}
      environment:
        TEENODE_API_TOKEN: "{{ teenode_api_token }}"

    - name: Create SSH key if not exists
      openssh_keypair:
        path: ~/.ssh/{{ app_name }}_key
        type: ed25519
      register: ssh_key

    - name: Add SSH key to Teenode
      shell: |
        teenode ssh-key add \
          --name "{{ app_name }}-ansible" \
          --key-file ~/.ssh/{{ app_name }}_key.pub
      when: ssh_key.changed

    - name: Create TEE VMs
      shell: |
        teenode vm create \
          --name {{ app_name }}-{{ item }} \
          --region {{ region }} \
          --cpu 4 \
          --memory 8192 \
          --ssh-key {{ app_name }}-ansible \
          --wait
      loop: "{{ range(1, 4) | list }}"
      register: vm_creation

    - name: Get VM IPs
      shell: |
        teenode vm describe {{ app_name }}-{{ item }} | grep "IP Address" | awk '{print $NF}'
      loop: "{{ range(1, 4) | list }}"
      register: vm_ips

    - name: Wait for VMs to be ready
      wait_for:
        host: "{{ item.stdout }}"
        port: 22
        delay: 10
        timeout: 120
      loop: "{{ vm_ips.results }}"

    - name: Configure VMs
      hosts: "{{ item.stdout }}"
      remote_user: root
      tasks:
        - name: Update system
          apt:
            update_cache: yes
            upgrade: safe

        - name: Install Docker
          shell: |
            curl -fsSL https://get.docker.com -o get-docker.sh
            sh get-docker.sh

        - name: Pull application image
          docker_image:
            name: "{{ docker_registry }}/{{ app_name }}"
            tag: "{{ app_version }}"
            state: present
            force: yes

        - name: Start application container
          docker_container:
            name: "{{ app_name }}"
            image: "{{ docker_registry }}/{{ app_name }}:{{ app_version }}"
            state: started
            restart_policy: always
            ports:
              - "3000:3000"
      loop: "{{ vm_ips.results }}"

    - name: Deploy application via Teenode
      shell: |
        teenode deployment create {{ app_name }} \
          --branch main \
          --wait

    - name: Verify deployment
      shell: |
        curl -f https://{{ app_name }}.teenode.app/health

    - name: Verify attestation
      shell: |
        teenode vm attest {{ app_name }} --verify

    - name: Create backup snapshot
      shell: |
        teenode vm snapshot create {{ app_name }} \
          --name backup-$(date +%Y%m%d)

Automated Testing Pipeline

#!/bin/bash
# automated-testing-pipeline.sh

set -e

PROJECT_NAME="my-app"
STAGING_URL="https://staging.my-app.teenode.app"
PROD_URL="https://my-app.teenode.app"

# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

log_success() { echo -e "\$GREEN✓ $1\$NC"; }
log_error() { echo -e "\$RED✗ $1\$NC"; }

# Unit tests
echo "Running unit tests..."
npm run test:unit || { log_error "Unit tests failed"; exit 1; }
log_success "Unit tests passed"

# Build Docker image
echo "Building Docker image..."
docker build -t my-app:test . || { log_error "Build failed"; exit 1; }
log_success "Docker image built"

# Deploy to staging
echo "Deploying to staging..."
teenode deployment create my-app-staging --branch main --wait || { log_error "Staging deployment failed"; exit 1; }
log_success "Deployed to staging"

# Wait for deployment
sleep 30

# Smoke tests on staging
echo "Running smoke tests on staging..."
curl -f $STAGING_URL/health || { log_error "Health check failed"; exit 1; }
curl -f $STAGING_URL/api/status || { log_error "API check failed"; exit 1; }
log_success "Smoke tests passed"

# Integration tests
echo "Running integration tests..."
npm run test:integration || { log_error "Integration tests failed"; exit 1; }
log_success "Integration tests passed"

# Performance tests
echo "Running performance tests..."
npm run test:performance || { log_error "Performance tests failed"; exit 1; }
log_success "Performance tests passed"

# Security tests
echo "Running security tests..."
npm run test:security || { log_error "Security tests failed"; exit 1; }
log_success "Security tests passed"

# Verify attestation on staging
echo "Verifying attestation..."
teenode vm attest my-app-staging --verify || { log_error "Attestation failed"; exit 1; }
log_success "Attestation verified"

# If all tests pass, approve for production
echo ""
echo "All tests passed! Ready for production deployment."
exit 0

Continuous Monitoring Setup

#!/bin/bash
# continuous-monitoring.sh

PROJECTS="my-app my-api my-worker"
CHECK_INTERVAL=300  # 5 minutes
ALERT_EMAIL="[email protected]"

while true; do
  TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
  echo "[$TIMESTAMP] Running health checks..."

  for project in $PROJECTS; do
    # Check deployment status
    STATUS=$(teenode project info "$project" | grep "Status:" | awk '{print $NF}')

    if [ "$STATUS" != "RUNNING" ]; then
      echo "ALERT: $project is $STATUS"
      echo "Project $project is not running (status: $STATUS)" | \
        mail -s "Teenode Alert: $project" "$ALERT_EMAIL"
    fi

    # Check metrics
    teenode project metrics "$project" | jq '{
      cpu_usage: .cpu_percent,
      memory_usage: .memory_percent,
      requests_per_sec: .requests_per_sec
    }' > /tmp/$project-metrics.json

    # Check for anomalies
    CPU=$(jq '.cpu_usage' /tmp/$project-metrics.json)
    if (( $(echo "\$CPU > 80" | bc -l) )); then
      echo "WARNING: $project CPU usage is \$CPU%"
    fi

    # Verify attestation
    if ! teenode vm attest "$project" --verify &>/dev/null; then
      echo "ALERT: Attestation verification failed for $project"
      echo "Attestation verification failed for $project" | \
        mail -s "Teenode Security Alert: $project" "\$ALERT_EMAIL"
    fi
  done

  sleep \$CHECK_INTERVAL
done

Best Practices

  • Secrets Management - Use provider-specific secret storage (GitHub Secrets, GitLab CI Variables, etc.)
  • Environment Separation - Use different tokens/credentials for each environment
  • Approval Gates - Require manual approval before production deployments
  • Rollback Strategy - Always maintain ability to quickly rollback
  • Monitoring - Set up continuous monitoring and alerting
  • Testing - Include comprehensive tests before deployments
  • Documentation - Document deployment procedures and troubleshooting
  • Audit Trail - Log all deployments and changes
Always test CI/CD pipelines in a staging environment before deploying to production.

Next Steps

You now have templates and examples for integrating Teenode with your CI/CD pipeline!