Skip to main content

ado-asana-sync

Test and Lint Quality Gate Status Coverage

Release PyPI - Version

Open in Visual Studio Code

This project aims to synchronize work items and pull requests between Azure DevOps (ADO) and Asana. It's currently in development and not ready for use. Breaking changes will occur as needed.

Setup and Configuration

This guide covers everything you need to configure and run the sync tool, either locally for development or via Docker.

Prerequisites

  • Python (if running locally): Install uv to manage dependencies and run the application.
  • Docker (if running via containers): Install Docker and Docker Compose.
  • Azure DevOps (ADO) PAT: A Personal Access Token with read access to Work Items, and Code (Read) access (which is required to sync pull requests).
  • Asana PAT: A Personal Access Token with access to the target workspace and projects.

Configuration

The application requires environment variables and a project mapping file.

1. Environment Variables

Create a .env file in the root directory. You can copy the example file to get started:

cp .env.example .env

Required Variables:

  • ADO_PAT: Your Personal Access Token for Azure DevOps.
  • ADO_URL: The full URL of your Azure DevOps instance (e.g., https://dev.azure.com/your-org).
  • ASANA_TOKEN: Your Personal Access Token for Asana.
  • ASANA_WORKSPACE_NAME: The exact name of your Asana workspace.

Optional Variables:

  • CLOSED_STATES: Comma-separated ADO states considered closed (default: Closed,Removed,Done).
  • THREAD_COUNT: Number of projects to sync in parallel (default: 8).
  • MAX_DEPTH: Maximum depth of nested subtasks to synchronize (default: 3).
  • SLEEP_TIME: Seconds to sleep between sync runs (default: 300).
  • RUN_ONCE: Run a single sync cycle and exit with a normal process status (default: false).
  • DRY_RUN: Compute and log planned create/update/close actions without writing to Asana or the local sync database. The log summary includes separate create/update/close counts and ADO/PR IDs for both work items and PR reviewer tasks. Because no local mappings are persisted, repeated dry runs re-evaluate the same items from scratch (default: false).
  • SYNC_THRESHOLD: Days to continue syncing closed tasks before unmapping (default: 30).
  • SYNCED_TAG_NAME: Asana tag appended to all synced items (default: synced).
  • LOGLEVEL: Console log level (default: INFO).
  • GROUP_REVIEWER_STRATEGY: How to handle ADO group/container reviewers (e.g. [Project]\Contributor). Options: ignore (default, skips them), default_user (assigns tasks to GROUP_REVIEWER_DEFAULT_USER), unassigned_task (creates an unassigned task with the group name), expand_group_members (resolves the group to individual members via the ADO Graph API and creates a task per member — requires vso.graph PAT scope).
  • GROUP_REVIEWER_DEFAULT_USER: Asana user email, GID, or display name to assign group reviewer tasks to when GROUP_REVIEWER_STRATEGY=default_user.
  • USER_MATCH_STRATEGY: How far to walk the ADO→Asana user matching ladder. Options: exact (exact email only), prefix (also match the email local part before the @), name (default, also match the normalized display name). An unmatched ADO assignee leaves the existing Asana assignee untouched rather than clearing it.
  • OTEL_TRACES_SAMPLER_ARG: Trace sampling percentage for Application Insights (e.g. 0.05 = 5%, 1.0 = 100%; default: 0.05).
  • APPINSIGHTS_LOGLEVEL: Minimum log level forwarded to Application Insights telemetry (default: WARNING).
  • APPINSIGHTS_SAMPLE_DEBUG / APPINSIGHTS_SAMPLE_INFO: Sampling rate for DEBUG/INFO logs sent to Application Insights (default: 0.05).
  • APPINSIGHTS_SAMPLE_WARNING / APPINSIGHTS_SAMPLE_ERROR / APPINSIGHTS_SAMPLE_CRITICAL: Sampling rate for WARNING/ERROR/CRITICAL logs (default: 1.0). These default to 100% to preserve incident visibility.

2. Project Mapping

The application needs to know which ADO teams map to which Asana projects. Create a projects.json file in the data/ directory:

cp data/projects.json.example data/projects.json

Mapping Structure:

[
  {
    "adoProjectName": "your-ado-project",
    "adoTeamName": "Backend Team",
    "asanaProjectName": "your-asana-project-backend"
  }
]
  • adoProjectName: The name of your Azure DevOps project.
  • adoTeamName: The specific team within the ADO project whose backlog you want to sync.
  • asanaProjectName: The corresponding Asana project name.

3. Persistence (Data Directory)

The application stores its sync state in a SQLite database at data/appdata.db. This file tracks which ADO items have been synced to Asana so that subsequent runs perform incremental updates rather than recreating everything from scratch.

Schema migrations are applied automatically on startup — no manual steps are required when upgrading. The database is currently at schema version 3. Hot-path lookups leverage SQLite JSON indexes on the ado_id, asana_gid, ado_pr_id, and reviewer_gid fields so that queries remain fast even as the sync history grows.

Important: Ensure the data/ directory is persisted across restarts (e.g. via a Docker volume or bind mount). Without this, the sync database is lost and all Asana tasks will be recreated on the next run.

Running the Application

You can run the sync tool either locally using uv or via Docker.

Option A: Running Locally (Development)

  1. Install dependencies:
    uv sync --dev
    
  2. Run the application:
    uv run python -m ado_asana_sync.sync
    
    Alternatively, use the installed script entry point defined in pyproject.toml:
    uv run ado-asana-sync
    
  3. Run a single dry-run or one-shot sync with inline variables:
    DRY_RUN=true RUN_ONCE=true uv run python -m ado_asana_sync.sync
    

The checked-in VS Code run profiles use the same uv workflow: they run uv sync --dev before launch and use the .venv environment that uv manages for the workspace.

In dry-run mode, expect log-only output such as create/update/close summaries. No Asana writes are sent, and no new entries are saved to data/appdata.db.

Option B: Running via Docker

The repository includes a compose.yml file for easy deployment.

  1. Build and start the container:
    docker compose up --build
    

To run in the background, append -d to the command. The container will automatically pick up your .env file and mount the data/projects.json configuration.

Verifying the Setup

Once running, the application will:

  1. Connect to Azure DevOps and Asana to validate credentials.
  2. Read the data/projects.json mapping.
  3. Begin synchronizing active work items and pull requests based on the mapping.
  4. Output logs indicating the sync progress.

You can verify the first sync by checking your mapped Asana project for newly created tasks with the configured synced tag.

Features

Work Item Synchronization

  • Synchronizes Azure DevOps work items (User Stories, Bugs, Tasks, etc.) to Asana tasks
  • Maintains bidirectional sync for updates, assignments, and status changes
  • Tiered user matching between ADO and Asana: exact email → email local part (before the @) → normalized display name (whitespace/order/comma tolerant), controlled by USER_MATCH_STRATEGY
  • An ADO assignee that cannot be matched to a current Asana user leaves the existing Asana assignee untouched (a warning is logged) rather than un-assigning the task
  • Configurable closed states mapping

Pull Request Synchronization

  • Synchronizes active Pull Requests from Azure DevOps to Asana
  • Creates separate reviewer tasks for each assigned reviewer
  • Task titles follow the format: "Pull Request 5: Update readme (Reviewer Name)"
  • Automatic status management:
    • Approved reviews (approve/approve with suggestions) → Close Asana task
    • Other review states (waiting for author, reject, no vote) → Keep task open
    • PR completion/abandonment → Close all reviewer tasks
    • Reviewer removal → Close reviewer's task
  • Handles reviewer additions, removals, and approval resets
  • Syncs PR title changes to Asana task titles

Pull Request Selection Logic

The system follows this logic to determine which PRs to sync:

  1. Repository Discovery: For each configured ADO project, discover all Git repositories
  2. Active PR Filtering: Query only PRs with status="active" (excludes completed/abandoned PRs)
  3. Reviewer Requirements: Only sync PRs that have at least one assigned reviewer
  4. User Matching: Only create tasks for reviewers who have matching Asana accounts, using the same tiered ladder as work item sync (exact email → email local part → normalized display name)
  5. Deduplication: Prevent duplicate reviewer processing by unique email identifier
  6. Cleanup Processing: Additionally process previously synced PRs that may now be closed/completed

Exclusion Criteria:

  • PRs without reviewers are skipped (logs: "No reviewers found for PR X")
  • Reviewers not found in Asana are skipped (logs: "PR X: reviewer Y not found in Asana")
  • Repositories/projects without Git API access are skipped gracefully

Changelog

See CHANGELOG.md for a detailed history of changes and new features.

Development

Commit message style

This repo uses Conventional Commits to ensure the build numbering is generated correctly

End-to-End Tests

The project includes a comprehensive end-to-end (E2E) test suite under tests/e2e/ that validates the complete sync workflow using mocked API endpoints and a real temporary SQLite database. These tests run automatically as part of the standard test suite.

Running E2E tests:

uv run pytest tests/e2e/ -v

What is tested:

Scenario Description
New work item ADO item not in Asana → task created
Work item update ADO item changed → Asana task updated
Work item close ADO item removed from backlog → Asana task completed
Work item reopen Closed ADO item returns to backlog → Asana task uncompleted
Subtask hierarchy Parent-child ADO relations → Asana subtask hierarchy
Preexisting match ADO item matches existing Asana task by name → no duplicate
PR open New PR with reviewer → reviewer task created
PR close PR completed/abandoned → reviewer task completed
PR reopen Reactivated PR → reviewer task uncompleted
PR status update Reviewer vote changed → Asana task updated

These tests are non-destructive: they use isolated temporary directories and mocked external APIs, so they never impact real databases or Asana/ADO workspaces.

Manual testing

To test the application manually, you can use the following steps:

Work Item Testing

  1. Create new ADO work item and ensure it is synced to Asana.
  2. Rename Asana task and ensure it is reverted back to the ADO name.
  3. Rename ADO task and ensure it is synced to Asana.
  4. Remove Synced tag from item in Asana and ensure it is replaced.
  5. Delete synced tag from Asana workspace and from appdata.json file and ensure it is re-created and assigned to all synced tasks.
  6. Mark Asana task as complete and ensure it is re-opened.
  7. Mark ADO task as complete and ensure it is marked as complete in Asana.
  8. Re-open ADO task and ensure it is re-opened in Asana.

Pull Request Testing

  1. Create new Pull Request in ADO with reviewers and ensure reviewer tasks are created in Asana.
  2. Change the PR title in ADO and ensure the title updates in Asana tasks on next sync.
  3. Add a reviewer to the PR and ensure a new task is created for them.
  4. Remove a reviewer from the PR and ensure their task is closed.
  5. Remove all reviewers from the PR and ensure all tasks are closed.
  6. Approve the PR as a reviewer and ensure the reviewer's task is closed.
  7. Approve with suggestions and ensure the reviewer's task is closed.
  8. Reject or request changes and ensure the reviewer's task remains open.
  9. Reset approval and ensure the reviewer's task is reopened.
  10. Complete/abandon the PR and ensure all reviewer tasks are closed.

Reference

ADO

Asana

Metadata

Release files for ado-asana-sync 1.28.10

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ado-asana-sync 1.28.10
File Size Uploaded Provenance Actions
ado_asana_sync-1.28.10.tar.gz 1.0 MB With provenance Details for ado_asana_sync-1.28.10.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ado-asana-sync 1.28.10
File Interpreter ABI Platform Actions
ado_asana_sync-1.28.10-py3-none-any.whl Python 3 none any Details for ado_asana_sync-1.28.10-py3-none-any.whl

Total release size: 1.1 MB

Release files / ado_asana_sync-1.28.10.tar.gz

Download URL ado_asana_sync-1.28.10.tar.gz
Size 1.0 MB
Tags Source
SHA-256 checksum
How to use checksums
5e7b2900a8a0944c2fe0ab5dfd0054843620931ed1e71c01983790cb65468d8b
BLAKE2b-256 checksum
How to use checksums
5feab41dbbef5a92ac9f1dafab767cf0aaa36fa8cf46392b7ebe399c7ceed6e9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Release files / ado_asana_sync-1.28.10-py3-none-any.whl

Download URL ado_asana_sync-1.28.10-py3-none-any.whl
Size 68.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0c83ad56587080b06009c3103991e3c6be37822cdbe2b1b75e42e92fd0831e01
BLAKE2b-256 checksum
How to use checksums
f0e36b237bcf2e54529f6442d3f873ba5ce20312e79822ebee95a8ae8d800e2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Security for ado-asana-sync 1.28.10

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ado_asana_sync-1.28.10.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.28.10 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page