Skip to main content

CI/CD Integration

Automate versioning and deployment. No manual tagging, no human error.

The Pattern

# .github/workflows/version.yml
on:
  push:
    branches: [main]

jobs:
  version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Need full history for tags

      - uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Install Edgit
        run: npm install -g @ensemble-edge/edgit

      - name: Auto-version changed components
        run: |
          # Detect changed components
          CHANGED=$(edgit discover scan --changed)

          # Auto-increment versions
          for component in $CHANGED; do
            LATEST=$(edgit tag list $component | tail -1)
            NEW_VERSION=$(edgit version bump $LATEST patch)
            edgit tag create $component $NEW_VERSION
          done

      - name: Push tags
        run: git push --tags

GitHub Actions

Auto-Version on Merge

name: Auto Version Components

on:
  push:
    branches: [main]

jobs:
  version:
    runs-on: ubuntu-latest
    permissions:
      contents: write  # Need write for tags

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Install Edgit
        run: npm install -g @ensemble-edge/edgit

      - name: Configure Git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

      - name: Version Components
        run: |
          # Get changed files
          CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD)

          # Version each changed component
          for file in $CHANGED_FILES; do
            COMPONENT=$(edgit discover detect $file --name-only)
            if [ ! -z "$COMPONENT" ]; then
              LATEST=$(edgit tag list $COMPONENT | tail -1 || echo "v0.0.0")
              NEW=$(edgit version bump $LATEST patch)
              edgit tag create $COMPONENT $NEW
              echo " Versioned $COMPONENT: $LATEST  $NEW"
            fi
          done

      - name: Push Tags
        run: git push --tags

Deploy to Staging on PR Merge

name: Deploy to Staging

on:
  pull_request:
    types: [closed]
    branches: [main]

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

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4

      - name: Install Edgit
        run: npm install -g @ensemble-edge/edgit

      - name: Deploy to Staging
        run: |
          # Get all components
          COMPONENTS=$(edgit components list --format json | jq -r '.[] | .name')

          # Deploy latest version of each to staging
          for component in $COMPONENTS; do
            LATEST=$(edgit tag list $component | tail -1)
            edgit deploy set $component $LATEST --to staging
          done

      - name: Push Deployment Tags
        run: git push --tags

Promote Staging to Production

name: Promote to Production

on:
  workflow_dispatch:  # Manual trigger
    inputs:
      components:
        description: 'Components to promote (comma-separated, or "all")'
        required: true
        default: 'all'

jobs:
  promote:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4

      - name: Install Edgit
        run: npm install -g @ensemble-edge/edgit

      - name: Promote to Production
        run: |
          if [ "${{ github.event.inputs.components }}" == "all" ]; then
            COMPONENTS=$(edgit components list --format json | jq -r '.[] | .name')
          else
            COMPONENTS=$(echo "${{ github.event.inputs.components }}" | tr ',' ' ')
          fi

          for component in $COMPONENTS; do
            # Get staging version
            STAGING_VERSION=$(edgit deploy get $component --from staging)

            # Deploy to production
            edgit deploy set $component $STAGING_VERSION --to prod

            echo " Promoted $component: $STAGING_VERSION  prod"
          done

      - name: Push Tags
        run: git push --tags

GitLab CI

# .gitlab-ci.yml
stages:
  - version
  - deploy

variables:
  GIT_DEPTH: 0  # Full history for tags

version:components:
  stage: version
  image: node:18
  only:
    - main
  script:
    - npm install -g @ensemble-edge/edgit
    - |
      # Configure git
      git config user.name "GitLab CI"
      git config user.email "ci@gitlab.com"

      # Version changed components
      CHANGED=$(git diff --name-only HEAD~1 HEAD)
      for file in $CHANGED; do
        COMPONENT=$(edgit discover detect $file --name-only)
        if [ ! -z "$COMPONENT" ]; then
          LATEST=$(edgit tag list $COMPONENT | tail -1 || echo "v0.0.0")
          NEW=$(edgit version bump $LATEST patch)
          edgit tag create $COMPONENT $NEW
        fi
      done

      # Push tags
      git push https://oauth2:${CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git --tags

deploy:staging:
  stage: deploy
  image: node:18
  only:
    - main
  script:
    - npm install -g @ensemble-edge/edgit
    - |
      # Deploy to staging
      COMPONENTS=$(edgit components list --format json | jq -r '.[] | .name')
      for component in $COMPONENTS; do
        LATEST=$(edgit tag list $component | tail -1)
        edgit deploy set $component $LATEST --to staging
      done
      git push https://oauth2:${CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git --tags

CircleCI

# .circleci/config.yml
version: 2.1

jobs:
  version:
    docker:
      - image: cimg/node:18.0
    steps:
      - checkout
      - run:
          name: Install Edgit
          command: npm install -g @ensemble-edge/edgit
      - run:
          name: Version Components
          command: |
            git config user.name "CircleCI"
            git config user.email "ci@circleci.com"

            CHANGED=$(git diff --name-only HEAD~1 HEAD)
            for file in $CHANGED; do
              COMPONENT=$(edgit discover detect $file --name-only)
              if [ ! -z "$COMPONENT" ]; then
                LATEST=$(edgit tag list $COMPONENT | tail -1 || echo "v0.0.0")
                NEW=$(edgit version bump $LATEST patch)
                edgit tag create $COMPONENT $NEW
              fi
            done
      - run:
          name: Push Tags
          command: git push --tags

workflows:
  version-and-deploy:
    jobs:
      - version:
          filters:
            branches:
              only: main

Semantic Versioning Automation

Auto-increment based on commit messages:
#!/bin/bash
# scripts/auto-version.sh

COMPONENT=$1
LATEST=$(edgit tag list $COMPONENT | tail -1 || echo "v0.0.0")

# Parse commit messages since last tag
COMMITS=$(git log --format=%s $(git describe --tags --abbrev=0)..HEAD -- components/$COMPONENT/)

if echo "$COMMITS" | grep -q "BREAKING CHANGE\|breaking:"; then
  # Major version bump
  NEW=$(edgit version bump $LATEST major)
elif echo "$COMMITS" | grep -q "feat:"; then
  # Minor version bump
  NEW=$(edgit version bump $LATEST minor)
else
  # Patch version bump
  NEW=$(edgit version bump $LATEST patch)
fi

edgit tag create $COMPONENT $NEW
echo "Versioned $COMPONENT: $LATEST  $NEW"
Use in CI:
- name: Auto Version
  run: |
    CHANGED=$(edgit discover scan --changed)
    for component in $CHANGED; do
      ./scripts/auto-version.sh $component
    done

Deploy Version Data to Cloudflare KV

Make versions available at the edge:
name: Deploy to Cloudflare

on:
  push:
    tags:
      - 'components/**'
      - 'agents/**'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4

      - name: Install Edgit
        run: npm install -g @ensemble-edge/edgit

      - name: Export Version Data
        run: |
          # Export all component versions to JSON
          edgit components list --format json > versions.json

      - name: Deploy to Cloudflare KV
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          KV_NAMESPACE_ID: ${{ secrets.KV_NAMESPACE_ID }}
        run: |
          # Upload versions to KV
          curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/storage/kv/namespaces/$KV_NAMESPACE_ID/values/component-versions" \
            -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
            -H "Content-Type: application/json" \
            --data @versions.json
Now Conductor can read versions from KV at runtime:
// Conductor reads from KV
const versions = await env.VERSIONS.get('component-versions', 'json');
const promptVersion = versions['extraction-prompt'].prod; // v1.0.0

Best Practices

1. Version on Main Branch Only

on:
  push:
    branches: [main]  # Only version after merge

2. Use Conventional Commits

feat: add new extraction logic      # Minor bump
fix: correct validation bug         # Patch bump
feat!: change API interface         # Major bump

3. Test Before Promoting

jobs:
  test:
    # Run tests

  version:
    needs: [test]  # Only version if tests pass

4. Protect Production Deploys

deploy:production:
  environment:
    name: production
    protection_rules:
      required_approvals: 2  # Require approvals

Next Steps