From e6c9d69381652a5187926c5e41791c0708595e19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Sep 2026 10:32:30 +0000 Subject: [PATCH 1/5] Initial plan From e27cde5f13ab610f280e50dfea26749acff9291c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Sep 2026 10:34:37 +0000 Subject: [PATCH 2/5] AI: Add daily open PR report workflow and script Co-authored-by: subrata-ms <141804867+subrata-ms@users.noreply.github.com> --- .github/workflows/daily-pr-report.yml | 40 ++++++++ scripts/daily_pr_report.py | 133 ++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 .github/workflows/daily-pr-report.yml create mode 100644 scripts/daily_pr_report.py diff --git a/.github/workflows/daily-pr-report.yml b/.github/workflows/daily-pr-report.yml new file mode 100644 index 000000000..a673c9f25 --- /dev/null +++ b/.github/workflows/daily-pr-report.yml @@ -0,0 +1,40 @@ +name: Daily Open PR Report + +on: + schedule: + # 09:00 IST == 03:30 UTC (GitHub Actions cron is UTC only) + - cron: "30 3 * * *" + workflow_dispatch: + +permissions: + contents: read + pull-requests: read + +jobs: + report: + name: Report open pull requests to Teams + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout code + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: '3.13' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install requests + + - name: Generate and send the open PR report + env: + GITHUB_TOKEN: ${{ github.token }} + TEAMS_WEBHOOK_URL: ${{ secrets.TEAMS_WEBHOOK_URL }} + TARGET_REPO: microsoft/mssql-python + run: python scripts/daily_pr_report.py diff --git a/scripts/daily_pr_report.py b/scripts/daily_pr_report.py new file mode 100644 index 000000000..5bcad5d77 --- /dev/null +++ b/scripts/daily_pr_report.py @@ -0,0 +1,133 @@ +"""Daily open pull request report for microsoft/mssql-python. + +Queries the GitHub REST API for every open pull request in the target +repository, builds a markdown summary and posts it to a Microsoft Teams +incoming webhook. + +Environment variables: + TARGET_REPO - "owner/repo" to inspect (default: microsoft/mssql-python) + GITHUB_TOKEN - token used for authenticated GitHub API calls (required) + TEAMS_WEBHOOK_URL - Teams incoming webhook URL (required) + +The webhook URL is never logged, only the resulting HTTP status is. +""" + +import logging +import os +import sys +from typing import Any, Dict, List + +import requests + +LOGGER = logging.getLogger("daily_pr_report") + +GITHUB_API = "https://api.github.com" +DEFAULT_REPO = "microsoft/mssql-python" +REQUEST_TIMEOUT = 30 +PER_PAGE = 100 +MAX_PAGES = 100 + + +def configure_logging() -> None: + """Configure console logging for the workflow run.""" + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s %(name)s: %(message)s", + stream=sys.stdout, + ) + + +def fetch_open_pull_requests(repo: str, token: str) -> List[Dict[str, Any]]: + """Return every open pull request for *repo*, following pagination.""" + headers = { + "Accept": "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + "Authorization": "Bearer " + token, + } + url = f"{GITHUB_API}/repos/{repo}/pulls" + + pulls: List[Dict[str, Any]] = [] + for page in range(1, MAX_PAGES + 1): + params = {"state": "open", "per_page": PER_PAGE, "page": page} + LOGGER.info("Fetching open pull requests page %d for %s", page, repo) + response = requests.get(url, headers=headers, params=params, timeout=REQUEST_TIMEOUT) + response.raise_for_status() + batch = response.json() + if not isinstance(batch, list): + raise ValueError(f"Unexpected GitHub API response type: {type(batch).__name__}") + pulls.extend(batch) + if len(batch) < PER_PAGE: + break + else: + LOGGER.warning("Stopped paginating after %d pages; report may be truncated.", MAX_PAGES) + + LOGGER.info("Found %d open pull request(s) in %s", len(pulls), repo) + return pulls + + +def build_report(repo: str, pulls: List[Dict[str, Any]]) -> str: + """Build the markdown report body for *pulls*.""" + repo_name = repo.split("/")[-1] + if not pulls: + return f"Daily {repo_name} report: No open pull requests found." + + lines = [ + f"## Daily {repo_name} Open PR Report", + "", + f"**Total Open PRs: {len(pulls)}**", + "", + ] + for pull in pulls: + author = (pull.get("user") or {}).get("login") or "unknown" + created = (pull.get("created_at") or "")[:10] + lines.append(f"- **PR #{pull.get('number')}: {pull.get('title') or '(no title)'}**") + lines.append(f" - Author: {author}") + lines.append(f" - Created: {created}") + lines.append(f" - URL: {pull.get('html_url') or ''}") + return "\n".join(lines) + + +def send_to_teams(webhook_url: str, report: str) -> None: + """Post *report* to the Teams incoming webhook.""" + LOGGER.info("Posting report to the Teams webhook") + response = requests.post(webhook_url, json={"text": report}, timeout=REQUEST_TIMEOUT) + response.raise_for_status() + LOGGER.info("Teams webhook responded with status %s", response.status_code) + + +def main() -> int: + """Generate the daily report and deliver it to Teams.""" + configure_logging() + + repo = os.environ.get("TARGET_REPO") or DEFAULT_REPO + token = os.environ.get("GITHUB_TOKEN", "") + webhook_url = os.environ.get("TEAMS_WEBHOOK_URL", "") + + if not token: + LOGGER.error("GITHUB_TOKEN is not set; cannot query the GitHub API.") + return 1 + if not webhook_url: + LOGGER.error("TEAMS_WEBHOOK_URL is not set; cannot deliver the report.") + return 1 + + try: + pulls = fetch_open_pull_requests(repo, token) + except (requests.RequestException, ValueError) as exc: + LOGGER.error("Failed to query the GitHub API: %s", exc) + return 1 + + report = build_report(repo, pulls) + LOGGER.info("Report generated:\n%s", report) + + try: + send_to_teams(webhook_url, report) + except requests.RequestException as exc: + LOGGER.error("Failed to post the report to Teams: %s", exc) + return 1 + + LOGGER.info("Daily PR report completed successfully.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 4a173827b268b2541703804e9e9995bbd8d2956d Mon Sep 17 00:00:00 2001 From: Subrata <141804867+subrata-ms@users.noreply.github.com> Date: Fri, 25 Sep 2026 16:10:16 +0530 Subject: [PATCH 3/5] Change warning to raise ValueError for pagination limit Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- scripts/daily_pr_report.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/daily_pr_report.py b/scripts/daily_pr_report.py index 5bcad5d77..8e53bb271 100644 --- a/scripts/daily_pr_report.py +++ b/scripts/daily_pr_report.py @@ -59,7 +59,9 @@ def fetch_open_pull_requests(repo: str, token: str) -> List[Dict[str, Any]]: if len(batch) < PER_PAGE: break else: - LOGGER.warning("Stopped paginating after %d pages; report may be truncated.", MAX_PAGES) + raise ValueError( + f"Stopped after {MAX_PAGES} pages; refusing to send a truncated report." + ) LOGGER.info("Found %d open pull request(s) in %s", len(pulls), repo) return pulls From d942aaa294e5fb2a730aa065ff24dca97c04dabb Mon Sep 17 00:00:00 2001 From: Subrata <141804867+subrata-ms@users.noreply.github.com> Date: Fri, 25 Sep 2026 16:10:28 +0530 Subject: [PATCH 4/5] Improve error logging for Teams report posting Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- scripts/daily_pr_report.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/daily_pr_report.py b/scripts/daily_pr_report.py index 8e53bb271..9a538d1b9 100644 --- a/scripts/daily_pr_report.py +++ b/scripts/daily_pr_report.py @@ -124,7 +124,11 @@ def main() -> int: try: send_to_teams(webhook_url, report) except requests.RequestException as exc: - LOGGER.error("Failed to post the report to Teams: %s", exc) + status = getattr(exc.response, "status_code", None) + if status is None: + LOGGER.error("Failed to post the report to Teams: %s", type(exc).__name__) + else: + LOGGER.error("Failed to post the report to Teams (HTTP status %s)", status) return 1 LOGGER.info("Daily PR report completed successfully.") From 538867adb655a336ad5eefa2e7505049c70ebeaa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Sep 2026 10:42:00 +0000 Subject: [PATCH 5/5] Remove workflow_dispatch trigger from daily PR report workflow Co-authored-by: subrata-ms <141804867+subrata-ms@users.noreply.github.com> --- .github/workflows/daily-pr-report.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/daily-pr-report.yml b/.github/workflows/daily-pr-report.yml index a673c9f25..371db71e8 100644 --- a/.github/workflows/daily-pr-report.yml +++ b/.github/workflows/daily-pr-report.yml @@ -4,7 +4,6 @@ on: schedule: # 09:00 IST == 03:30 UTC (GitHub Actions cron is UTC only) - cron: "30 3 * * *" - workflow_dispatch: permissions: contents: read