Skip to content

CI/CD Setup

Automate Jira syncing with GitHub Actions, GitLab CI, or other CI/CD platforms.

Duration: ~6 minutes

⚙️

GitHub Actions Setup Demo

Overview

Automatically sync your markdown epics to Jira whenever changes are pushed:

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Push to main   │ ──▶ │  GitHub Action  │ ──▶ │  Jira Updated   │
│  (EPIC.md)      │     │  runs spectryn   │     │  automatically  │
└─────────────────┘     └─────────────────┘     └─────────────────┘

Step 1: Add Secrets to GitHub

1. Go to Repository Settings

Settings → Secrets and variables → Actions

2. Add Repository Secrets
  • JIRA_URL
  • JIRA_EMAIL
  • JIRA_API_TOKEN
3. Add Repository Variables
  • EPIC_KEY (e.g., PROJ-123)

Step 2: Create Workflow File

bash
$ mkdir -p .github/workflows

$ cat > .github/workflows/jira-sync.yml << 'EOF'
name: Sync to Jira

on:
  push:
    paths:
      - 'docs/EPIC.md'
    branches:
      - main

jobs:
  sync:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      
      - name: Install spectryn
        run: pip install spectryn
      
      - name: Sync to Jira
        env:
          JIRA_URL: ${{ secrets.JIRA_URL }}
          JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
          JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
        run: |
          spectryn \
            --markdown docs/EPIC.md \
            --epic ${{ vars.EPIC_KEY }} \
            --execute \
            --no-confirm \
            --export results.json
      
      - name: Upload sync results
        uses: actions/upload-artifact@v4
        with:
          name: jira-sync-results
          path: results.json
EOF

Step 3: Test the Workflow

bash
# Make a change to EPIC.md
$ echo "Updated: $(date)" >> docs/EPIC.md

# Commit and push
$ git add docs/EPIC.md .github/workflows/jira-sync.yml
$ git commit -m "docs: update epic, add CI sync"
$ git push origin main

# Watch the action run at:
# https://github.com/your-org/your-repo/actions

Workflow Execution

Run spectryn \
  --markdown docs/EPIC.md \
  --epic PROJ-123 \
  --execute \
  --no-confirm \
  --export results.json

╭──────────────────────────────────────────────────────────────╮
│  spectryn v1.0.0                                              │
│  Syncing: docs/EPIC.md → PROJ-123                            │
│  Mode: EXECUTE (CI/CD)                                       │
╰──────────────────────────────────────────────────────────────╯

📋 Found 3 stories in markdown

Syncing stories ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 3/3

✓ PROJ-124: Updated description, 3 subtasks
✓ PROJ-128: Updated description, 2 subtasks
✓ PROJ-131: Updated description, 2 subtasks

╭──────────────────────────────────────────────────────────────╮
│  ✅ Sync Complete                                            │
│  Stories: 3 | Subtasks: 7 | Duration: 3.8s                   │
╰──────────────────────────────────────────────────────────────╯

Advanced: PR Preview

Add a workflow that shows what would change on pull requests:

yaml
# .github/workflows/jira-preview.yml
name: Preview Jira Changes

on:
  pull_request:
    paths:
      - 'docs/EPIC.md'

jobs:
  preview:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      
      - run: pip install spectryn
      
      - name: Generate preview
        id: preview
        env:
          JIRA_URL: ${{ secrets.JIRA_URL }}
          JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
          JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
        run: |
          output=$(spectryn \
            --markdown docs/EPIC.md \
            --epic ${{ vars.EPIC_KEY }} \
            --output json)
          
          stories=$(echo "$output" | jq '.summary.stories')
          subtasks=$(echo "$output" | jq '.summary.subtasks_to_create')
          
          echo "stories=$stories" >> $GITHUB_OUTPUT
          echo "subtasks=$subtasks" >> $GITHUB_OUTPUT
      
      - name: Comment on PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## 📋 Jira Sync Preview
              
              When merged, this PR will sync to **${{ vars.EPIC_KEY }}**:
              
              | Metric | Count |
              |--------|-------|
              | Stories | ${{ steps.preview.outputs.stories }} |
              | Subtasks to create | ${{ steps.preview.outputs.subtasks }} |
              
              ✅ Changes will be applied automatically when merged to main.`
            })

Advanced: Multi-Epic Sync

Sync multiple epics from a monorepo:

yaml
# .github/workflows/multi-sync.yml
name: Sync All Epics

on:
  push:
    paths:
      - 'docs/epics/**/*.md'
    branches:
      - main

jobs:
  detect:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.detect.outputs.matrix }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2
      
      - id: detect
        run: |
          changed=$(git diff --name-only HEAD~1 HEAD | grep "docs/epics/.*\.md" || echo "")
          if [ -z "$changed" ]; then
            echo "matrix=[]" >> $GITHUB_OUTPUT
          else
            matrix="["
            for file in $changed; do
              # Extract epic key from frontmatter or filename
              epic=$(grep -m1 "^epic:" "$file" | awk '{print $2}' || basename "$file" .md)
              matrix="$matrix{\"file\":\"$file\",\"epic\":\"$epic\"},"
            done
            matrix="${matrix%,}]"
            echo "matrix=$matrix" >> $GITHUB_OUTPUT
          fi

  sync:
    needs: detect
    if: needs.detect.outputs.matrix != '[]'
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include: ${{ fromJson(needs.detect.outputs.matrix) }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install spectryn
      - name: Sync ${{ matrix.epic }}
        env:
          JIRA_URL: ${{ secrets.JIRA_URL }}
          JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
          JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
        run: |
          spectryn -m "${{ matrix.file }}" -e "${{ matrix.epic }}" -x --no-confirm

Slack Notifications

Add Slack notifications for sync results:

yaml
- name: Notify Slack
  if: always()
  uses: slackapi/slack-github-action@v1
  with:
    payload: |
      {
        "text": "${{ job.status == 'success' && '✅' || '❌' }} Jira sync ${{ job.status }}",
        "blocks": [
          {
            "type": "section",
            "text": {
              "type": "mrkdwn",
              "text": "*Jira Sync ${{ job.status == 'success' && 'Complete' || 'Failed' }}*\n\nEpic: `${{ vars.EPIC_KEY }}`\nCommit: ${{ github.sha }}"
            }
          }
        ]
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Troubleshooting

Common Issues

IssueSolution
Authentication failedCheck secrets are set correctly
File not foundVerify path in --markdown flag
Epic not foundEnsure epic exists in Jira
TimeoutAdd retry logic or increase timeout

Debug Mode

yaml
- name: Sync with debug
  run: |
    spectryn \
      --markdown docs/EPIC.md \
      --epic ${{ vars.EPIC_KEY }} \
      --execute \
      --no-confirm \
      --verbose \
      --log-format json

What's Next?

Released under the MIT License.