From 201eb34765de73924c40a1c38b2c06a98ef83dd2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:34:55 +0000 Subject: [PATCH 1/8] feat: Parallelize folder data fetching - Use ThreadPoolExecutor to fetch folder data concurrently - Reduces startup time significantly by parallelizing network I/O - Fix SyntaxError in create_folder where positional arg followed keyword arg --- main.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 0355a19..20972b2 100644 --- a/main.py +++ b/main.py @@ -18,6 +18,7 @@ import os import logging import time +import concurrent.futures from typing import Dict, List, Optional, Any, Set, Sequence import httpx @@ -227,9 +228,9 @@ def create_folder(client: httpx.Client, profile_id: str, name: str, do: int, sta """ try: _api_post( + client, f"{API_BASE}/{profile_id}/groups", data={"name": name, "do": do, "status": status}, - client, ) # Re-fetch the list and pick the folder we just created @@ -333,12 +334,19 @@ def sync_profile( try: # Fetch all folder data first folder_data_list = [] - for url in folder_urls: + + def safe_fetch(url): try: - folder_data_list.append(fetch_folder_data(url)) + return fetch_folder_data(url) except (httpx.HTTPError, KeyError) as e: log.error(f"Failed to fetch folder data from {url}: {e}") - continue + return None + + # Fetch folder data in parallel to speed up startup + with concurrent.futures.ThreadPoolExecutor() as executor: + results = executor.map(safe_fetch, folder_urls) + + folder_data_list = [r for r in results if r is not None] if not folder_data_list: log.error("No valid folder data found") From 80ed6867907aab33da0f4f17239eb65a11325bd4 Mon Sep 17 00:00:00 2001 From: Abhi Mehrotra Date: Fri, 12 Dec 2025 18:23:36 -0600 Subject: [PATCH 2/8] Update main.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 20972b2..3db4538 100644 --- a/main.py +++ b/main.py @@ -343,7 +343,8 @@ def safe_fetch(url): return None # Fetch folder data in parallel to speed up startup - with concurrent.futures.ThreadPoolExecutor() as executor: + max_workers = min(10, len(folder_urls)) if folder_urls else 1 + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: results = executor.map(safe_fetch, folder_urls) folder_data_list = [r for r in results if r is not None] From 6ab3e6f83600e5567eb20cc7e841741d84c2a677 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 00:42:33 +0000 Subject: [PATCH 3/8] Initial plan From 90edba7b1c00b2fca7233fa1fb6d3cb3805dd51a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 00:47:21 +0000 Subject: [PATCH 4/8] Initial plan From f418f5b1bb6c02fa1291097d5cb212f73aca6b35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 00:49:42 +0000 Subject: [PATCH 5/8] Improve URL validation with logging and early return for empty valid URLs Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com> --- main.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 019a19a..5f89044 100644 --- a/main.py +++ b/main.py @@ -354,6 +354,14 @@ def sync_profile( # Validate URLs first valid_urls = [url for url in folder_urls if validate_folder_url(url)] + + invalid_count = len(folder_urls) - len(valid_urls) + if invalid_count > 0: + log.warning(f"Filtered out {invalid_count} invalid URL(s)") + + if not valid_urls: + log.error("No valid folder URLs to fetch") + return False def safe_fetch(url): try: @@ -363,7 +371,7 @@ def safe_fetch(url): return None # Fetch folder data in parallel to speed up startup - max_workers = min(10, len(valid_urls)) if valid_urls else 1 + max_workers = min(10, len(valid_urls)) with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: results = executor.map(safe_fetch, valid_urls) From 06760202533f0747c78894ae5bc268220abfe3d9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 00:51:12 +0000 Subject: [PATCH 6/8] Fix Python version and dependency management inconsistencies Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com> --- .github/workflows/sync.yml | 11 +++++------ README.md | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 3d94e1f..0488117 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -14,17 +14,16 @@ jobs: - name: Checkout repo uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 + - name: Set up uv + uses: astral-sh/setup-uv@v3 with: - python-version: '3.12' - cache: 'pip' + python-version: "3.13" - name: Install dependencies - run: pip install httpx python-dotenv + run: uv sync --frozen - name: Run sync script env: TOKEN: ${{ secrets.TOKEN }} PROFILE: ${{ secrets.PROFILE }} - run: python main.py + run: uv run python main.py diff --git a/README.md b/README.md index d15c930..1b99066 100644 --- a/README.md +++ b/README.md @@ -69,5 +69,5 @@ The included GitHub Actions workflow (`.github/workflows/ci.yml`) runs a dry-run - `PROFILE`: your Control D profile ID(s) ## Requirements -- Python 3.12+ +- Python 3.13+ - `uv` (for dependency management) From 5925c3884128c24d1417ad20798afc286342cab9 Mon Sep 17 00:00:00 2001 From: Abhi Mehrotra Date: Fri, 12 Dec 2025 18:55:24 -0600 Subject: [PATCH 7/8] Update main.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/main.py b/main.py index 5f89044..b2d78b0 100644 --- a/main.py +++ b/main.py @@ -526,7 +526,6 @@ def main(): "status": "✅ Success" if status else "❌ Failed", }) - if args.plan_json: with open(args.plan_json, "w", encoding="utf-8") as f: json.dump(plan, f, indent=2) From a08b97a51a982529339cae526a6e280f31ce4560 Mon Sep 17 00:00:00 2001 From: Abhi Mehrotra Date: Fri, 12 Dec 2025 18:55:30 -0600 Subject: [PATCH 8/8] Update main.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/main.py b/main.py index b2d78b0..32b008b 100644 --- a/main.py +++ b/main.py @@ -543,7 +543,6 @@ def main(): ) print("=" * 80 + "\n") - total = len(profile_ids or ["dry-run-placeholder"]) log.info(f"All profiles processed: {success_count}/{total} successful") exit(0 if success_count == total else 1)