summaryrefslogtreecommitdiffstats
path: root/.github/scripts/analytics
diff options
context:
space:
mode:
authorKirill Rysin <[email protected]>2026-06-18 21:47:37 +0200
committerGitHub <[email protected]>2026-06-18 22:47:37 +0300
commit42b2c71601e033e050d6fa76d674a0ca2cc562a0 (patch)
treeefb11fc1990f44d244e6ea406311e053c8b8aa57 /.github/scripts/analytics
parent8e6218277715bd372ab944a75b6a496292fe77cc (diff)
Export release:* labels to info.releaseblocker_state for BI (#43893)
Co-authored-by: Cursor <[email protected]>
Diffstat (limited to '.github/scripts/analytics')
-rw-r--r--.github/scripts/analytics/data_mart_queries/datalens_ds_queries/github_issues_timeline_full.sql2
-rw-r--r--.github/scripts/analytics/data_mart_queries/github_issues_timeline.sql2
-rwxr-xr-x.github/scripts/analytics/export_issues_to_ydb.py95
3 files changed, 77 insertions, 22 deletions
diff --git a/.github/scripts/analytics/data_mart_queries/datalens_ds_queries/github_issues_timeline_full.sql b/.github/scripts/analytics/data_mart_queries/datalens_ds_queries/github_issues_timeline_full.sql
index bb4c511a813..9c2711e5c4e 100644
--- a/.github/scripts/analytics/data_mart_queries/datalens_ds_queries/github_issues_timeline_full.sql
+++ b/.github/scripts/analytics/data_mart_queries/datalens_ds_queries/github_issues_timeline_full.sql
@@ -50,6 +50,7 @@ SELECT
i.max_branch AS max_branch,
i.env AS env,
i.priority AS priority,
+ i.releaseblocker_state AS releaseblocker_state,
i.branch AS branch,
i.area AS area,
CAST(
@@ -100,6 +101,7 @@ CROSS JOIN (
COALESCE(JSON_VALUE(t.info, "$.max_branch"), '-') AS max_branch,
COALESCE(JSON_VALUE(t.info, "$.env"), 'env:-') AS env,
COALESCE(JSON_VALUE(t.info, "$.priority"), 'priority:-') AS priority,
+ COALESCE(JSON_VALUE(t.info, "$.releaseblocker_state"), 'release:-') AS releaseblocker_state,
COALESCE(JSON_VALUE(t.info, "$.branch"), '-') AS branch,
COALESCE(JSON_VALUE(t.info, "$.area"), 'area/-') AS area
FROM `github_data/issues` AS t
diff --git a/.github/scripts/analytics/data_mart_queries/github_issues_timeline.sql b/.github/scripts/analytics/data_mart_queries/github_issues_timeline.sql
index e2469b7d366..cbece481272 100644
--- a/.github/scripts/analytics/data_mart_queries/github_issues_timeline.sql
+++ b/.github/scripts/analytics/data_mart_queries/github_issues_timeline.sql
@@ -60,6 +60,7 @@ SELECT
i.max_branch AS max_branch,
i.env AS env,
i.priority AS priority,
+ i.releaseblocker_state AS releaseblocker_state,
i.branch AS branch,
i.area AS area,
CAST(
@@ -110,6 +111,7 @@ CROSS JOIN (
COALESCE(JSON_VALUE(t.info, "$.max_branch"), '-') AS max_branch,
COALESCE(JSON_VALUE(t.info, "$.env"), 'env:-') AS env,
COALESCE(JSON_VALUE(t.info, "$.priority"), 'priority:-') AS priority,
+ COALESCE(JSON_VALUE(t.info, "$.releaseblocker_state"), 'release:-') AS releaseblocker_state,
COALESCE(JSON_VALUE(t.info, "$.branch"), '-') AS branch,
Coalesce(m.matched_area,
CASE
diff --git a/.github/scripts/analytics/export_issues_to_ydb.py b/.github/scripts/analytics/export_issues_to_ydb.py
index 15d0d4c49d1..0dcbe6a04ce 100755
--- a/.github/scripts/analytics/export_issues_to_ydb.py
+++ b/.github/scripts/analytics/export_issues_to_ydb.py
@@ -11,33 +11,73 @@ import requests
from typing import List, Dict, Any, Optional
from ydb_wrapper import YDBWrapper
-# Configuration
ORG_NAME = 'ydb-platform'
REPO_NAME = 'ydb'
PROJECT_ID = None #'45' # Optional: set to None to skip project data
+ISSUES_PAGE_SIZE = 50 # smaller pages → smaller GraphQL payloads (avoids truncated JSON)
# YDB configuration is now handled by ydb_wrapper
+_RETRYABLE_GITHUB_STATUS_CODES = frozenset({429, 500, 502, 503, 504})
+_GITHUB_QUERY_MAX_RETRIES = 8
+_GITHUB_QUERY_INITIAL_BACKOFF_SEC = 2.0
+_GITHUB_QUERY_TIMEOUT_SEC = 120
+
+
+def _github_query_retry_wait(attempt: int, backoff: float, reason: str) -> float:
+ print(f"{reason}, retry {attempt + 1}/{_GITHUB_QUERY_MAX_RETRIES} in {backoff:.0f}s...")
+ time.sleep(backoff)
+ return min(backoff * 2, 60)
+
+
def run_query(query: str, variables: Optional[Dict] = None) -> Dict[str, Any]:
- """Execute GraphQL query against GitHub API"""
- GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
- HEADERS = {"Authorization": f"Bearer {GITHUB_TOKEN}", "Content-Type": "application/json"}
-
- request = requests.post(
- 'https://api.github.com/graphql',
- json={'query': query, 'variables': variables},
- headers=HEADERS
- )
-
- if request.status_code == 200:
- response = request.json()
- if 'errors' in response:
- for error in response['errors']:
- print(f"GraphQL Error: {error.get('message', 'Unknown error')}")
- raise Exception(f"GraphQL Error: {error.get('message', 'Unknown error')}")
- return response
- else:
+ """Execute GraphQL query against GitHub API (retries transient 5xx/429 and truncated JSON)."""
+ github_token = os.environ["GITHUB_TOKEN"]
+ headers = {"Authorization": f"Bearer {github_token}", "Content-Type": "application/json"}
+ payload = {'query': query, 'variables': variables}
+
+ backoff = _GITHUB_QUERY_INITIAL_BACKOFF_SEC
+ for attempt in range(_GITHUB_QUERY_MAX_RETRIES + 1):
+ try:
+ request = requests.post(
+ 'https://api.github.com/graphql',
+ json=payload,
+ headers=headers,
+ timeout=_GITHUB_QUERY_TIMEOUT_SEC,
+ )
+ except requests.RequestException as exc:
+ if attempt >= _GITHUB_QUERY_MAX_RETRIES:
+ raise
+ backoff = _github_query_retry_wait(
+ attempt, backoff, f"GitHub API request error ({exc})"
+ )
+ continue
+
+ if request.status_code == 200:
+ try:
+ response = request.json()
+ except ValueError as exc:
+ if attempt >= _GITHUB_QUERY_MAX_RETRIES:
+ raise
+ backoff = _github_query_retry_wait(
+ attempt,
+ backoff,
+ f"GitHub API truncated/invalid JSON ({exc})",
+ )
+ continue
+ if 'errors' in response:
+ for error in response['errors']:
+ print(f"GraphQL Error: {error.get('message', 'Unknown error')}")
+ raise Exception(f"GraphQL Error: {response['errors'][0].get('message', 'Unknown error')}")
+ return response
+
+ if request.status_code in _RETRYABLE_GITHUB_STATUS_CODES and attempt < _GITHUB_QUERY_MAX_RETRIES:
+ backoff = _github_query_retry_wait(
+ attempt, backoff, f"GitHub API {request.status_code}"
+ )
+ continue
+
raise Exception(f"Query failed with status {request.status_code}: {request.text}")
def get_last_update_time(ydb_wrapper: YDBWrapper, table_path: str) -> Optional[datetime]:
@@ -192,7 +232,7 @@ def fetch_repository_issues(org_name: str = ORG_NAME, repo_name: str = REPO_NAME
{
organization(login: "%s") {
repository(name: "%s") {
- issues(first: 100, after: %s, orderBy: {field: UPDATED_AT, direction: DESC}%s) {
+ issues(first: %d, after: %s, orderBy: {field: UPDATED_AT, direction: DESC}%s) {
nodes {
id
number
@@ -282,7 +322,7 @@ def fetch_repository_issues(org_name: str = ORG_NAME, repo_name: str = REPO_NAME
total_fetched = 0
while has_next_page:
- query = repository_issues_query % (org_name, repo_name, end_cursor, since_filter)
+ query = repository_issues_query % (org_name, repo_name, ISSUES_PAGE_SIZE, end_cursor, since_filter)
result = run_query(query)
if result and 'data' in result:
@@ -557,6 +597,7 @@ def transform_issues_for_ydb(issues: List[Dict[str, Any]], project_fields: Optio
branch_labels = []
env = None
priority = None
+ releaseblocker_state = None
area = None
for label in issue.get('labels', {}).get('nodes', []):
name = label.get('name', '')
@@ -574,12 +615,22 @@ def transform_issues_for_ydb(issues: List[Dict[str, Any]], project_fields: Optio
# priority detection
if name.startswith('prio:'):
priority = name
+ # release blocker detection
+ if name.startswith('release:'):
+ releaseblocker_state = name
# area detection
if name.startswith('area/'):
area = name
branch = ';'.join(branch_labels) if branch_labels else None
max_branch = get_max_branch(branch_labels) if branch_labels else None
- info = {'branch': branch, 'max_branch': max_branch, 'env': env, 'priority': priority, 'area': area}
+ info = {
+ 'branch': branch,
+ 'max_branch': max_branch,
+ 'env': env,
+ 'priority': priority,
+ 'releaseblocker_state': releaseblocker_state,
+ 'area': area,
+ }
proj_list = projects_for_info_json(issue)
if proj_list:
info['projects'] = proj_list