# CI/CD Deployment Reference — setup-dlt-uv-connector-airflow

Detailed CI/CD integration guides for deploying Airflow with baked-in dlt connectors.

## GitHub Actions — complete workflow template

```yaml
name: Deploy Airflow

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure SSH for connectors repo
        env:
          DEPLOY_KEY: ${{ secrets.CONNECTORS_DEPLOY_KEY }}
          DEPLOY_KEY_PASSPHRASE: ${{ secrets.CONNECTORS_DEPLOY_KEY_PASSPHRASE }}
        run: |
          mkdir -p ~/.ssh
          ssh-keyscan github.com >> ~/.ssh/known_hosts
          echo "$DEPLOY_KEY" > /tmp/deploy_key
          chmod 600 /tmp/deploy_key
          printf '#!/bin/sh\necho "%s"\n' "$DEPLOY_KEY_PASSPHRASE" > /tmp/askpass.sh
          chmod +x /tmp/askpass.sh
          eval "$(ssh-agent -s)"
          SSH_ASKPASS=/tmp/askpass.sh SSH_ASKPASS_REQUIRE=force ssh-add /tmp/deploy_key
          rm -f /tmp/deploy_key /tmp/askpass.sh

      - name: Sync dlt connectors
        run: ./sync-connectors.sh

      - name: Build production image
        run: |
          docker build \
            --target prod_build \
            --build-arg arg_airflow__core__fernet_key=${{ secrets.AIRFLOW_FERNET_KEY }} \
            --build-arg arg_airflow__core__sql_alchemy_conn=${{ secrets.AIRFLOW_SQL_ALCHEMY_CONN }} \
            --build-arg arg_airflow__smtp__smtp_password=${{ secrets.AIRFLOW_SMTP_PASSWORD }} \
            --build-arg arg_airflow__webserver__secret_key=${{ secrets.AIRFLOW_WEBSERVER_SECRET_KEY }} \
            -t airflow-prod:${{ github.sha }} \
            .

      # Push to your container registry and deploy as needed
      # - name: Push to registry
      #   run: docker push ...
      # - name: Deploy to server
      #   run: ...
```

## Deploy key setup

The `sync-connectors.sh` step requires the CI runner to have SSH access to the connectors monorepo. For GitHub-hosted runners accessing a GitHub repo, add a passphrase-encrypted deploy key:

1. Generate a passphrase-encrypted SSH key pair: `ssh-keygen -t ed25519 -C "ci-deploy-key" -f deploy_key`
2. Add the **public key** as a deploy key in the connectors monorepo (Settings -> Deploy keys, read-only)
3. Add two secrets in the Airflow repo:
   - `CONNECTORS_DEPLOY_KEY` — the private key contents
   - `CONNECTORS_DEPLOY_KEY_PASSPHRASE` — the passphrase used to encrypt the key
4. Configure SSH in the workflow before the sync step. Use `ssh-agent` with an `SSH_ASKPASS` helper script to supply the passphrase non-interactively:

```yaml
      - name: Configure SSH for connectors repo
        env:
          DEPLOY_KEY: ${{ secrets.CONNECTORS_DEPLOY_KEY }}
          DEPLOY_KEY_PASSPHRASE: ${{ secrets.CONNECTORS_DEPLOY_KEY_PASSPHRASE }}
        run: |
          mkdir -p ~/.ssh
          ssh-keyscan github.com >> ~/.ssh/known_hosts
          echo "$DEPLOY_KEY" > /tmp/deploy_key
          chmod 600 /tmp/deploy_key
          printf '#!/bin/sh\necho "%s"\n' "$DEPLOY_KEY_PASSPHRASE" > /tmp/askpass.sh
          chmod +x /tmp/askpass.sh
          eval "$(ssh-agent -s)"
          SSH_ASKPASS=/tmp/askpass.sh SSH_ASKPASS_REQUIRE=force ssh-add /tmp/deploy_key
          rm -f /tmp/deploy_key /tmp/askpass.sh
```

## GitLab CI — complete pipeline template

If the client uses GitLab, fork the connectors repo to GitLab (see `sync-dlt-connectors` skill for details) and use this pipeline:

```yaml
# .gitlab-ci.yml
stages:
  - build
  - deploy

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - apk add --no-cache git bash python3 curl
    - curl -LsSf https://astral.sh/uv/install.sh | sh
    - export PATH="$HOME/.local/bin:$PATH"
  script:
    - ./sync-connectors.sh
    - docker build --target prod_build -t airflow-prod:$CI_COMMIT_SHA .
    # Push to registry, deploy, etc.
```

GitLab CI runners automatically have access to repos in the same group, so no extra SSH setup is needed if the connectors fork lives in the same GitLab group.
