9.3 KiB
| name | client | status | tech | local_path | deploy | url | tags | created | server | port | db | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| DevOps ↔ ClickUp Sync | Oliver Internal | active |
|
/Users/ai_leed/Documents/Projects/Oliver/DevOps_Click_UP_sync | docker compose up -d --build | http://localhost:8080 |
|
2026-04-14 | local | 8080 | SQLite |
Overview
DevOps_Click_UP_sync is a FastAPI webhook service that bi-directionally synchronises Azure DevOps work items with ClickUp tasks in real time. It creates, updates, and mirrors comments and attachments across both platforms, preventing echo-event loops through deduplication logic. The service exposes a simple web dashboard for managing project mappings and viewing sync history, and is deployable as a Docker container with SQLite persistence.
Tech Stack
- Frontend: Vanilla JavaScript + HTML + Tailwind CSS (dashboard at
frontend/index.html) - Backend: Python 3.x + FastAPI + Uvicorn + Pydantic
- Database: SQLite with SQLAlchemy ORM (async via
aiosqlite) - Infrastructure: Docker Compose (single service, volume-mounted data directory)
- AI/ML: N/A
- Key libraries:
httpx(async HTTP client for ADO/ClickUp REST APIs),markdownify(HTML↔Markdown conversion),python-dotenv(env config),aiofiles(async file I/O)
Architecture
The service is a synchronisation engine that listens for webhooks from both Azure DevOps and ClickUp, then mirrors changes bidirectionally:
-
Webhook Handlers (
src/api/webhooks.py):POST /webhooks/ado— receives ADO work item eventsPOST /webhooks/clickup— receives ClickUp task events- HMAC validation via
WEBHOOK_SECRETprevents spoofing
-
Sync Engine (
src/sync/engine.py):- Core logic that determines what to create, update, or skip
- Calls Field Mapper to translate between ADO and ClickUp schemas
- Invokes REST clients to push changes back to origin system
-
Field Mapper (
src/sync/mapper.py):- Transforms ADO work item fields ↔ ClickUp task fields
- Handles title, description, state, assignee, priority, custom fields
- Converts HTML ↔ Markdown for comment bodies
-
Echo-Event Deduplication (
src/sync/dedup.py):- Maintains a
deduptable: tracks (entity_id, direction, timestamp) - Incoming webhook is skipped if the same entity was synced from the opposite direction within a TTL window (prevents A→B→A loops)
- Maintains a
-
REST Clients:
src/clients/ado.py— Azure DevOps REST API (work items, comments, attachments)src/clients/clickup.py— ClickUp API (tasks, comments, attachments)
-
Database Models (
src/database.py):SyncMap— maps ADO work item ID ↔ ClickUp task IDProjectMap— maps ADO project ↔ ClickUp space/folderCommentMap— maps ADO comment ↔ ClickUp comment for updatesDedupLog— echo-event prevention records
-
Dashboard APIs (
src/api/dashboard.py):GET /api/dashboard/stats— sync counts and healthGET /api/dashboard/project-maps— view configured mappingsGET /api/dashboard/sync-log— paginated sync history
-
Setup API (
src/api/setup.py):POST /api/setup/register-webhooks— auto-registers webhook subscriptions with ADO/ClickUp
┌─────────────┐ ┌──────────────┐
│ Azure │ │ ClickUp │
│ DevOps │ │ │
└──────┬──────┘ └──────┬───────┘
│ │
│ POST /webhooks/ado │ POST /webhooks/clickup
│ (work item created/updated) │ (task created/updated)
│ │
└────────────┬─────────────────────────┘
│
┌───────▼────────┐
│ FastAPI │
│ Service │
│ (port 8080) │
└───────┬────────┘
│
┌────────────┼────────────┐
│ │ │
│ Sync │ Dedup │
│ Engine │ Check │
│ │ │
└──┬─────────┴────────┬───┘
│ │
┌─────▼──────┐ ┌──────▼────┐
│ Field │ │ Database │
│ Mapper │ │ (SQLite) │
│ │ │ │
└─────┬──────┘ └──────┬────┘
│ │
┌─────▼──────────────────▼────┐
│ REST Clients │
│ (ADO + ClickUp APIs) │
└──────────────────────────────┘
Dev Commands
# Copy and configure environment variables
cp .env.example .env
# → Edit .env with ADO_PAT, CLICKUP_API_TOKEN, WEBHOOK_SECRET, PUBLIC_URL, etc.
# Start service in Docker (builds image, mounts ./data volume, runs on port 8080)
docker compose up -d
# Rebuild after code changes
docker compose up -d --build
# View live logs
docker compose logs -f sync-service
# Health check
curl http://localhost:8080/api/health
# Run locally without Docker (requires venv + pip install -r requirements.txt)
pip install -r requirements.txt
uvicorn src.main:app --host 0.0.0.0 --port 8080 --reload
# Access dashboard in browser
http://localhost:8080/
Deployment
- Server: Unknown (no server hostname provided in config)
- Deploy:
docker compose up -d --build(from project directory) - URL: Controlled by
PUBLIC_URLenvironment variable (must be HTTPS and reachable by ADO and ClickUp for webhook callbacks) - Port: 8080
- Service: Docker Compose service named
sync-service(not a systemd service) - Local path:
/Users/ai_leed/Documents/Projects/Oliver/DevOps_Click_UP_sync - Data persistence: SQLite database stored in
./data/sync.db(Docker volume./data:/app/data) - Health check: Built-in Docker healthcheck (
GET /api/healthevery 30s)
Environment Variables
ADO_ORGANIZATION— Azure DevOps organization name (required)ADO_PROJECT— Azure DevOps project name (required)ADO_PAT— Azure DevOps Personal Access Token with work item read/write scopes (required)ADO_ASSIGNED_TO— WIQL filter for assigned-to field; leave empty to sync all items, use@Meto sync all (optional)CLICKUP_API_TOKEN— ClickUp API token (format:pk_...); requiredCLICKUP_WORKSPACE_ID— ClickUp workspace ID (required)WEBHOOK_SECRET— Random secret for HMAC signature validation of incoming webhooks (required; must not bechangemeor echo-event dedup will not activate)PUBLIC_URL— Public HTTPS URL where this service is reachable (required for webhook registration; e.g.,https://your-domain.com)LOG_LEVEL— Logging level for uvicorn/app (DEBUG,INFO,WARNING,ERROR; default:INFO)DATABASE_URL— SQLAlchemy database URL (default:sqlite+aiosqlite:///./data/sync.db); supports PostgreSQL or MySQL if changed
API / Endpoints
Webhooks (External)
POST /webhooks/ado— Azure DevOps webhook receiver (validates HMAC withWEBHOOK_SECRET)POST /webhooks/clickup— ClickUp webhook receiver (validates HMAC withWEBHOOK_SECRET)
Setup
POST /api/setup/register-webhooks— Automatically registers webhook subscriptions with ADO and ClickUp (idempotent)
Dashboard
GET /— Servefrontend/index.html(vanilla JS dashboard)GET /api/health— Health check endpoint (used by Docker healthcheck)GET /api/dashboard/stats— Sync statistics (total synced, last sync time, error count)GET /api/dashboard/project-maps— List all configured ADO↔ClickUp project mappingsGET /api/dashboard/sync-log?page=1&limit=20— Paginated sync history log
Config
- Routes defined in
src/main.py(routers fromwebhooks.py,setup.py,dashboard.py)
Known Issues
- No test suite — no unit or integration tests configured; no linting setup
- Production hardening needed — before deploying to production:
- Add pytest + test coverage
- Add black/flake8 linting
- Validate all exception handling (especially in sync engine)
- Add request rate-limiting
- Implement robust logging/monitoring
- SSH access constraint — no SSH to server without explicit user instruction
- Field mapping gaps — some custom fields may not be handled; ADO→ClickUp HTML→Markdown conversion may lose formatting in edge cases
- **No automatic webhook re-registration
Sessions
2026-04-14 – Project catalogued
Done: Added to Obsidian second brain.
Change Log
| Date | Requested | Changed | Files |
|---|---|---|---|
| 2026-04-14 | Initial setup | Note created | — |