# CircleCI Integration

Load KeyEnv secrets into your CircleCI workflows using the official orb.

Source: https://keyenv.dev/docs/guides/circleci/

Load secrets from KeyEnv into your CircleCI workflows using the official KeyEnv orb or CLI.

## Overview

There are two ways to integrate KeyEnv with CircleCI:

| Method | Best For | Setup |
|--------|----------|-------|
| **KeyEnv Orb** | Most projects | Simplest setup |
| **CLI-based** | Custom requirements | More control |

## Features

- Exports secrets as environment variables
- Optionally writes secrets to a `.env` file
- Supports project-scoped service tokens
- Works with self-hosted KeyEnv instances
- Zero dependencies (auto-installs required tools)

---

## Option 1: Using the KeyEnv Orb (Recommended)

The official KeyEnv orb provides the simplest integration.

### Basic Usage

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0

jobs:
  build:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - keyenv/load:
          environment: production
      - run: npm test
```

### Orb Parameters

| Parameter | Description | Required | Default |
|-----------|-------------|----------|---------|
| `environment` | Environment name (e.g., `production`) | Yes | - |
| `project-id` | Project ID. Optional if using project-scoped token. | No | - |
| `token-var` | Name of env var containing the token | No | `KEYENV_TOKEN` |
| `api-url` | KeyEnv API URL | No | `https://api.keyenv.dev` |
| `env-file` | Path to write `.env` file | No | - |

### With Project ID

If your service token isn't project-scoped, specify the project:

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0

jobs:
  build:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - keyenv/load:
          environment: staging
          project-id: proj_abc123def456
      - run: npm test
```

### Write to .env File

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0

jobs:
  build:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - keyenv/load:
          environment: production
          env-file: .env
      - run: |
          # Secrets are available as env vars AND in .env file
          npm test
```

### Multi-Environment Workflow

Use different environments for different jobs:

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0

jobs:
  test:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - keyenv/load:
          environment: development
      - run: npm ci
      - run: npm test

  deploy:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - keyenv/load:
          environment: production
      - run: npm run deploy

workflows:
  build-and-deploy:
    jobs:
      - test
      - deploy:
          requires:
            - test
          filters:
            branches:
              only: main
```

### Using a Context

Store your `KEYENV_TOKEN` in a CircleCI context for use across multiple projects:

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0

jobs:
  deploy:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - keyenv/load:
          environment: production
      - run: npm run deploy

workflows:
  deploy:
    jobs:
      - deploy:
          context: keyenv-secrets
```

### Using the load-and-run Job

For simple workflows, use the built-in job:

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0

workflows:
  deploy:
    jobs:
      - keyenv/load-and-run:
          environment: production
          command: npm run deploy
          context: keyenv-secrets
```

---

## Option 2: CLI-Based Integration

Install the KeyEnv CLI directly in your workflow for more control.

### Basic Example

```yaml
# .circleci/config.yml
version: 2.1

jobs:
  deploy:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: Install KeyEnv CLI
          command: |
            curl -fsSL https://keyenv.dev/install.sh | bash
            echo 'export PATH="$HOME/.keyenv/bin:$PATH"' >> $BASH_ENV
      - run:
          name: Pull secrets
          command: keyenv pull -e production
      - run:
          name: Deploy
          command: ./deploy.sh

workflows:
  deploy:
    jobs:
      - deploy:
          filters:
            branches:
              only: main
```

### Using keyenv run

Inject secrets directly without writing to disk:

```yaml
# .circleci/config.yml
version: 2.1

jobs:
  deploy:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: Install KeyEnv CLI
          command: |
            curl -fsSL https://keyenv.dev/install.sh | bash
            echo 'export PATH="$HOME/.keyenv/bin:$PATH"' >> $BASH_ENV
      - run:
          name: Deploy with secrets
          command: keyenv run -e production -- ./deploy.sh

workflows:
  deploy:
    jobs:
      - deploy:
          filters:
            branches:
              only: main
```

### Reusable Commands

```yaml
# .circleci/config.yml
version: 2.1

commands:
  install-keyenv:
    description: Install KeyEnv CLI
    steps:
      - run:
          name: Install KeyEnv CLI
          command: |
            curl -fsSL https://keyenv.dev/install.sh | bash
            echo 'export PATH="$HOME/.keyenv/bin:$PATH"' >> $BASH_ENV

  load-secrets:
    description: Load secrets from KeyEnv
    parameters:
      environment:
        type: string
        default: "production"
    steps:
      - run:
          name: Pull secrets
          command: keyenv pull -e << parameters.environment >>

jobs:
  test:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - install-keyenv
      - load-secrets:
          environment: development
      - run: npm ci
      - run: npm test

  deploy:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - install-keyenv
      - run:
          name: Deploy
          command: keyenv run -e production -- npm run deploy

workflows:
  build-and-deploy:
    jobs:
      - test
      - deploy:
          requires:
            - test
          filters:
            branches:
              only: main
```

---

## Language-Specific Examples

### Node.js Application

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0
  node: circleci/node@5.0

jobs:
  test:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - node/install-packages
      - keyenv/load:
          environment: development
      - run: npm test
      - run: npm run build

workflows:
  ci:
    jobs:
      - test
```

### Python Application

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0
  python: circleci/python@2.0

jobs:
  test:
    docker:
      - image: cimg/python:3.12
    steps:
      - checkout
      - python/install-packages
      - keyenv/load:
          environment: development
      - run: pytest

workflows:
  ci:
    jobs:
      - test
```

### Go Application

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0
  go: circleci/go@1.7

jobs:
  test:
    docker:
      - image: cimg/go:1.22
    steps:
      - checkout
      - go/load-cache
      - go/mod-download
      - keyenv/load:
          environment: development
      - run: go test ./...
      - go/save-cache

workflows:
  ci:
    jobs:
      - test
```

### Docker Build with Secrets

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0

jobs:
  build:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - setup_remote_docker
      - keyenv/load:
          environment: production
          env-file: .env.production
      - run: |
          docker build \
            --secret id=env,src=.env.production \
            -t myapp:latest .

workflows:
  build:
    jobs:
      - build
```

---

## Setting Up Your Token

### Create a Service Token

1. Go to your KeyEnv dashboard
2. Navigate to **Settings > Service Tokens**
3. Create a new token with:
   - **Scope**: Select your project (recommended)
   - **Permissions**: `secrets:read`
4. Copy the token

### Add Token to CircleCI

**Option A: Project Environment Variable**

1. Go to your CircleCI project
2. Navigate to **Project Settings** > **Environment Variables**
3. Add a new variable:
   - Name: `KEYENV_TOKEN`
   - Value: Your service token

**Option B: Context (Recommended for multiple projects)**

1. Go to **Organization Settings** > **Contexts**
2. Create a new context (e.g., `keyenv-secrets`)
3. Add a variable:
   - Name: `KEYENV_TOKEN`
   - Value: Your service token
4. Use the context in your workflow:

```yaml
workflows:
  deploy:
    jobs:
      - deploy:
          context: keyenv-secrets
```

---

## Self-Hosted KeyEnv

If you're running a self-hosted KeyEnv instance:

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0

jobs:
  deploy:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - keyenv/load:
          environment: production
          api-url: https://keyenv.your-company.com
      - run: npm run deploy
```

For CLI-based integration:

```yaml
# .circleci/config.yml
version: 2.1

jobs:
  deploy:
    docker:
      - image: cimg/base:stable
    environment:
      KEYENV_API_URL: https://keyenv.your-company.com
    steps:
      - checkout
      - run:
          name: Install KeyEnv CLI
          command: |
            curl -fsSL https://keyenv.dev/install.sh | bash
            echo 'export PATH="$HOME/.keyenv/bin:$PATH"' >> $BASH_ENV
      - run:
          name: Deploy
          command: keyenv run -e production -- ./deploy.sh
```

---

## Security Best Practices

1. **Use contexts** - Centralize token management across projects
2. **Scope tokens narrowly** - Create separate tokens for each environment
3. **Don't log secrets** - Avoid `echo $SECRET` in your scripts
4. **Use `keyenv run`** - Prefer runtime injection over writing to disk
5. **Restrict context access** - Limit which projects can use each context

> **Warning**
>
> Never commit service tokens to your repository. Always use CircleCI's environment variables or contexts.

---

## Troubleshooting

### "Authentication failed"

- Verify your token is correct in CircleCI project settings
- Check the token hasn't expired
- Ensure the environment variable name matches `token-var` parameter

### "Access denied"

- The token may not have access to the specified project
- The token may not have access to the specified environment
- Check token permissions in KeyEnv dashboard

### "Project or environment not found"

- Verify the `project-id` is correct
- Verify the `environment` name matches exactly (case-sensitive)
- Check the project/environment exists in KeyEnv

### Secrets not available in subsequent steps

- Ensure you're using a bash-based executor (most CircleCI images work)
- Secrets are exported to `$BASH_ENV` and loaded automatically in subsequent steps
- If using a custom shell, source `$BASH_ENV` manually

### "keyenv: command not found"

Ensure the PATH is updated correctly:

```yaml
- run:
    name: Install KeyEnv CLI
    command: |
      curl -fsSL https://keyenv.dev/install.sh | bash
      echo 'export PATH="$HOME/.keyenv/bin:$PATH"' >> $BASH_ENV
```

---

## Complete Example

Here's a complete CircleCI configuration for a Node.js application:

```yaml
# .circleci/config.yml
version: 2.1

orbs:
  keyenv: keyenv/secrets@1.0
  node: circleci/node@5.0

jobs:
  test:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - node/install-packages
      - keyenv/load:
          environment: development
      - run:
          name: Run tests
          command: npm test
      - run:
          name: Run linter
          command: npm run lint
      - store_test_results:
          path: test-results

  build:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - node/install-packages
      - keyenv/load:
          environment: staging
      - run:
          name: Build application
          command: npm run build
      - persist_to_workspace:
          root: .
          paths:
            - dist

  deploy_staging:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - attach_workspace:
          at: .
      - keyenv/load:
          environment: staging
      - run:
          name: Deploy to staging
          command: npm run deploy:staging

  deploy_production:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - attach_workspace:
          at: .
      - keyenv/load:
          environment: production
      - run:
          name: Deploy to production
          command: npm run deploy:production

workflows:
  build-test-deploy:
    jobs:
      - test
      - build:
          requires:
            - test
      - deploy_staging:
          requires:
            - build
          filters:
            branches:
              only: develop
          context: keyenv-secrets
      - deploy_production:
          requires:
            - build
          filters:
            branches:
              only: main
          context: keyenv-secrets
```

### CircleCI Context Setup

Create a context named `keyenv-secrets`:

| Variable | Value |
|----------|-------|
| `KEYENV_TOKEN` | `srv_your_token_here` |

### KeyEnv Environments

Create environments in KeyEnv:

| Environment | Purpose |
|-------------|---------|
| `development` | Testing with test data |
| `staging` | Pre-production validation |
| `production` | Live production deployment |

---

## Additional Resources

- [CircleCI Orb Registry](https://circleci.com/developer/orbs/orb/keyenv/secrets)
- [CircleCI Orb Documentation](https://circleci.com/docs/orb-intro/)
- [KeyEnv GitHub Action](/docs/sdks/github-action) - Similar integration for GitHub Actions
