# CI/CD Integration Guide

> Source: https://agilitycms.com/docs/developers/cli-ci-cd-integration-guide

This guide explains how to integrate the Agility CLI into your CI/CD pipelines to automate content synchronization between Agility CMS instances.

## Overview

The typical CI/CD workflow involves:

1. **Pull** the repository containing your Agility mappings
2. **Run** the CLI sync command to synchronize content between instances
3. **Push** the updated mappings back to the repository

Persisting mappings in a Git repository ensures that content relationships are maintained across sync operations and prevents duplicate content creation.

---

## Prerequisites

### 1. Personal Access Token (PAT)



In CI/CD environments, browser-based authentication is not available. You must use a **Personal Access Token (PAT)** for authentication.

**To obtain a PAT:** Personal Access Tokens are created and managed using the management API which are documented in the "Personal Access Tokens" section of our Management API Swagger documentation found here: https://mgmt.aglty.io/index.html


### 2. Required Permissions

The user associated with the PAT must have one of the following roles:

- Org Admin
- Instance Admin
- Manager role on both source and target instances

### 3. Instance GUIDs

You'll need the GUIDs for both your source and target instances:

- **Source GUID**: The instance you're pulling content from
- **Target GUID**: The instance you're pushing content to

Find these in **Settings** → **Instance Details** in your Agility CMS dashboard.

---

## Environment Variables

Configure these environment variables in your CI/CD platform:

| Variable | Required | Description |
|----------|----------|-------------|
| `AGILITY_TOKEN` | Yes | Personal Access Token for authentication |
| `AGILITY_GUID` | Yes | Source instance GUID |
| `AGILITY_TARGET_GUID` | Yes | Target instance GUID |
| `AGILITY_LOCALES` | No | Comma-separated locales (e.g., `en-us,fr-ca`) |
| `AGILITY_ELEMENTS` | No | Elements to sync (default: all) |
| `AGILITY_ROOT_PATH` | No | Local directory for files (default: `agility-files`) |

---

## Headless Mode

When running in CI/CD, use the `--headless` flag to:

- Disable interactive console output
- Log all output to files instead
- Prevent terminal UI elements that may cause issues in non-TTY environments

```bash
agility sync --headless --sourceGuid="$SOURCE_GUID" --targetGuid="$TARGET_GUID"
```

---

## Sample Pipeline Configurations

### GitHub Actions

```yaml
name: Agility CMS Sync

on:
  # Trigger manually or on schedule
  workflow_dispatch:
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM UTC

env:
  AGILITY_TOKEN: ${{ secrets.AGILITY_TOKEN }}
  SOURCE_GUID: ${{ vars.AGILITY_SOURCE_GUID }}
  TARGET_GUID: ${{ vars.AGILITY_TARGET_GUID }}

jobs:
  sync:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install Agility CLI
        run: npm install -g @agility/cli

      - name: Run Agility Sync
        run: |
          agility sync \
            --headless \
            --sourceGuid="$SOURCE_GUID" \
            --targetGuid="$TARGET_GUID"

      - name: Commit and push mappings
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          
          # Add only the mappings directory
          git add agility-files/mappings/
          
          # Check if there are changes to commit
          if git diff --staged --quiet; then
            echo "No mapping changes to commit"
          else
            git commit -m "chore: update Agility CMS mappings [skip ci]"
            git push
          fi
```

### GitHub Actions - Multi-Environment

```yaml
name: Agility CMS Multi-Environment Sync

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options:
          - staging
          - production

jobs:
  sync:
    runs-on: ubuntu-latest
    environment: ${{ github.event.inputs.environment }}
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install Agility CLI
        run: npm install -g @agility/cli

      - name: Run Agility Sync
        env:
          AGILITY_TOKEN: ${{ secrets.AGILITY_TOKEN }}
        run: |
          agility sync \
            --headless \
            --sourceGuid="${{ vars.AGILITY_SOURCE_GUID }}" \
            --targetGuid="${{ vars.AGILITY_TARGET_GUID }}" \
            --locales="${{ vars.AGILITY_LOCALES }}"

      - name: Commit and push mappings
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          
          git add agility-files/mappings/
          
          if git diff --staged --quiet; then
            echo "No mapping changes to commit"
          else
            git commit -m "chore: update Agility CMS mappings (${{ github.event.inputs.environment }}) [skip ci]"
            git push
          fi
```


---

## Advanced Configuration

### Selective Sync by Elements

Sync only specific content types:

```bash
agility sync \
  --headless \
  --sourceGuid="$SOURCE_GUID" \
  --targetGuid="$TARGET_GUID" \
  --elements="Models,Content,Assets"
```

Available elements: `Models`, `Galleries`, `Assets`, `Containers`, `Content`, `Templates`, `Pages`

### Model-Specific Sync

Sync specific models with their dependencies:

```bash
agility sync \
  --headless \
  --sourceGuid="$SOURCE_GUID" \
  --targetGuid="$TARGET_GUID" \
  --models-with-deps="BlogPost,BlogCategory"
```

### Locale-Specific Sync

Sync only specific locales:

```bash
agility sync \
  --headless \
  --sourceGuid="$SOURCE_GUID" \
  --targetGuid="$TARGET_GUID" \
  --locales="en-us,fr-ca"
```

---

## Workflow Operations

After syncing content, you can perform workflow operations (publish, approve, etc.) on the synced items:

```bash
# Publish all synced content and pages
agility workflows \
  --headless \
  --sourceGuid="$SOURCE_GUID" \
  --targetGuid="$TARGET_GUID" \
  --operationType="publish"
```

Available operations: `publish`, `unpublish`, `approve`, `decline`, `requestApproval`

### Full Sync + Publish Pipeline Example

```yaml
# GitHub Actions example with sync and publish
- name: Sync and Publish Content
  run: |
    # First, sync the content
    agility sync \
      --headless \
      --sourceGuid="$SOURCE_GUID" \
      --targetGuid="$TARGET_GUID"
    
    # Then, publish the synced content
    agility workflows \
      --headless \
      --sourceGuid="$SOURCE_GUID" \
      --targetGuid="$TARGET_GUID" \
      --operationType="publish"
```

---

## Best Practices

### 1. Store Mappings in Version Control

Always commit your `agility-files/mappings/` directory to your repository. This ensures:

- Consistency across pipeline runs
- Rollback capability
- Audit trail of sync operations
- Prevention of duplicate content creation

### 2. Use `[skip ci]` in Commit Messages

Include `[skip ci]` in mapping commit messages to prevent infinite pipeline loops:

```bash
git commit -m "chore: update Agility CMS mappings [skip ci]"
```

### 3. Separate Environments

Use different branches or repositories for different environment mappings:

```
main branch       → Production sync mappings
staging branch    → Staging sync mappings
develop branch    → Development sync mappings
```

### 4. Scheduled Runs

Set up scheduled pipeline runs for regular sync operations:

```yaml
# GitHub Actions cron syntax: minute hour day month weekday
schedule:
  - cron: '0 2 * * *'  # Daily at 2 AM UTC
```

### 5. Artifact Retention

Keep sync logs as artifacts for debugging:

```yaml
artifacts:
  paths:
    - agility-files/logs/
  expire_in: 7 days
```

### 6. Handle Failures Gracefully

Use the exit code to handle sync failures:

```bash
agility sync --headless --sourceGuid="..." --targetGuid="..." || {
  echo "Sync failed, check logs for details"
  exit 1
}
```

---

## Troubleshooting

### Authentication Errors

- Verify the PAT is valid and not expired
- Ensure the token has sufficient permissions
- Check that the `AGILITY_TOKEN` secret is properly configured

### SSL Certificate Errors

In corporate environments with proxy servers, use the `--insecure` flag:

```bash
agility sync --headless --insecure --sourceGuid="..." --targetGuid="..."
```

### Missing Mappings

If mappings are missing, the CLI will create new content instead of updating existing content. Always ensure:

- The `agility-files/mappings/` directory is committed to your repository
- The pipeline checks out the full repository (not a shallow clone)

### Duplicate Content

If duplicates are being created:

1. Stop all concurrent sync operations
2. Restore mappings from a known good state
3. Run a fresh sync with `--update=true` to rebuild mappings

---

## Support

- **Documentation**: [Agility CMS Doc Site](https://agilitycms.com/docs)
- **Community**: [Agility Slack](https://join.slack.com/t/agilitycommunity/shared_invite/enQtNzI2NDc3MzU4Njc2LWI2OTNjZTI3ZGY1NWRiNTYzNmEyNmI0MGZlZTRkYzI3NmRjNzkxYmI5YTZjNTg2ZTk4NGUzNjg5NzY3OWViZGI)
- **Issues**: [GitHub Issues](https://github.com/agility/agility-cms-management-cli/issues)
