From f427192c0f34c1f0461af31b77eb17054bd6947e Mon Sep 17 00:00:00 2001 From: selmankoc Date: Thu, 11 Jun 2026 14:37:02 +0300 Subject: [PATCH 1/5] feat: add script to format studio release notes This script processes raw release notes, cleans up unnecessary information, and generates a user-friendly summary. It removes markdown headers, deduplicates entries, and limits the output to a specified number of lines for better readability. --- .../scripts/format-studio-release-notes.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/scripts/format-studio-release-notes.py diff --git a/.github/scripts/format-studio-release-notes.py b/.github/scripts/format-studio-release-notes.py new file mode 100644 index 0000000000..c42beb5b59 --- /dev/null +++ b/.github/scripts/format-studio-release-notes.py @@ -0,0 +1,68 @@ +import os +import re + +raw = os.environ.get("RAW_NOTES", "") +lines = raw.splitlines() + +output = [] +seen = set() + + +def clean_line(text: str) -> str: + text = text.strip() + if not text: + return "" + + # Drop markdown headers/changelog lines. + if re.match(r"^#+\s", text, flags=re.I): + return "" + if re.match(r"^\*\*?\s*full\s+changelog", text, flags=re.I): + return "" + if re.match(r"^full\s+changelog", text, flags=re.I): + return "" + + text = re.sub(r"^[\s\-*•]+", "", text) + text = re.sub(r"\s+by\s+@?[a-zA-Z0-9_-]+\s+in\s+https?://\S+", "", text) + text = re.sub(r"\s+by\s+@?[a-zA-Z0-9_-]+\s*$", "", text) + text = re.sub(r"@([a-zA-Z0-9_-]+)", "", text) + text = re.sub(r"\s*\([^)]*#\d+\)\s*$", "", text) + text = re.sub(r"\s+#\d+\s*$", "", text) + text = re.sub(r"\s+", " ", text).strip(" .:-") + + if len(text) < 8: + return "" + + # Make user-friendly short title + summary when possible. + if ":" in text: + left, right = [p.strip() for p in text.split(":", 1)] + left = left[:40].rstrip(" .") + right_words = right.split() + right = " ".join(right_words[:14]).rstrip(" .") + text = f"{left}: {right}" if right else left + else: + words = text.split() + if len(words) > 16: + text = " ".join(words[:16]).rstrip(" .") + + return text + + +for line in lines: + cleaned = clean_line(line) + if not cleaned: + continue + + # Normalize casing and deduplicate. + cleaned = cleaned[0].upper() + cleaned[1:] if cleaned else cleaned + key = cleaned.lower() + if key in seen: + continue + seen.add(key) + + output.append(f"* {cleaned}") + if len(output) >= 8: + break + +os.makedirs(".tmp", exist_ok=True) +with open(".tmp/final-notes.txt", "w", encoding="utf-8") as f: + f.write("\n".join(output)) From 3a9dc19caa6b341ff34e68497002b37ed0724c1c Mon Sep 17 00:00:00 2001 From: selmankoc Date: Thu, 11 Jun 2026 14:37:10 +0300 Subject: [PATCH 2/5] feat: add script to update studio version mapping This script automates the process of updating the version mapping for ABP Studio in the documentation. It checks the provided studio and ABP versions, validates them, and updates the version mapping table accordingly, ensuring that existing ranges are extended or new mappings are added as necessary. --- .../scripts/update-studio-version-mapping.py | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 .github/scripts/update-studio-version-mapping.py diff --git a/.github/scripts/update-studio-version-mapping.py b/.github/scripts/update-studio-version-mapping.py new file mode 100644 index 0000000000..d22ee65574 --- /dev/null +++ b/.github/scripts/update-studio-version-mapping.py @@ -0,0 +1,117 @@ +import os +import re +from packaging.version import Version, InvalidVersion + +studio_ver = os.environ["STUDIO_VERSION"] +abp_ver = os.environ["ABP_VERSION"] +file_path = "docs/en/studio/version-mapping.md" + +try: + studio = Version(studio_ver) +except InvalidVersion: + print(f"❌ Invalid Studio version: {studio_ver}") + raise SystemExit(1) + +with open(file_path, "r") as f: + lines = f.readlines() + +# Find table start (skip SEO and headers) +table_start = 0 +table_end = 0 +for i, line in enumerate(lines): + if line.strip().startswith("|") and "**ABP Studio Version**" in line: + table_start = i + elif table_start > 0 and line.strip() and not line.strip().startswith("|"): + table_end = i + break + +if table_start == 0: + print("❌ Could not find version mapping table") + raise SystemExit(1) + +# If no end found, table goes to end of file +if table_end == 0: + table_end = len(lines) + +# Extract sections +before_table = lines[:table_start] +table_header = lines[table_start : table_start + 2] +data_rows = [l for l in lines[table_start + 2 : table_end] if l.strip().startswith("|")] +after_table = lines[table_end:] + +new_rows = [] +handled = False + + +def parse_version_range(version_str): + """Parse '2.1.5 - 2.1.9' or '2.1.5' into (start, end)""" + version_str = version_str.strip() + + if "–" in version_str or "-" in version_str: + parts = re.split(r"\s*[–-]\s*", version_str) + if len(parts) == 2: + try: + return Version(parts[0].strip()), Version(parts[1].strip()) + except InvalidVersion: + return None, None + + try: + v = Version(version_str) + return v, v + except InvalidVersion: + return None, None + + +def format_row(studio_range, abp_version): + """Format a table row with proper spacing""" + return f"| {studio_range:<22} | {abp_version:<27} |\n" + + +# Process existing rows +for row in data_rows: + match = re.match(r"\|\s*(.+?)\s*\|\s*(.+?)\s*\|", row) + if not match: + continue + + existing_studio_range = match.group(1).strip() + existing_abp = match.group(2).strip() + + if existing_abp != abp_ver: + new_rows.append(row) + continue + + start_ver, end_ver = parse_version_range(existing_studio_range) + + if start_ver is None or end_ver is None: + new_rows.append(row) + continue + + if start_ver <= studio <= end_ver: + print(f"✅ Studio version {studio_ver} already covered in range {existing_studio_range}") + handled = True + new_rows.append(row) + elif end_ver < studio: + if ( + start_ver.major == studio.major + and start_ver.minor == studio.minor + and studio.micro <= end_ver.micro + 5 + ): + new_range = f"{start_ver} - {studio}" + new_rows.append(format_row(new_range, abp_ver)) + print(f"✅ Extended range: {new_range}") + handled = True + else: + new_rows.append(row) + else: + new_rows.append(row) + +if not handled: + new_row = format_row(str(studio), abp_ver) + new_rows.insert(0, new_row) + print(f"✅ Added new mapping: {studio_ver} -> {abp_ver}") + +with open(file_path, "w") as f: + f.writelines(before_table) + f.writelines(table_header) + f.writelines(new_rows) + f.writelines(after_table) From 04786974a45edf1154d8f26ec12f25144d21679f Mon Sep 17 00:00:00 2001 From: selmankoc Date: Thu, 11 Jun 2026 14:37:19 +0300 Subject: [PATCH 3/5] refactor: replace inline Python scripts with dedicated scripts for formatting release notes and updating version mapping This update simplifies the workflow by utilizing separate Python scripts for generating user-friendly release notes and managing the version mapping for ABP Studio. The previous inline scripts have been removed to enhance maintainability and clarity. --- .github/workflows/update-studio-docs.yml | 200 +---------------------- 1 file changed, 2 insertions(+), 198 deletions(-) diff --git a/.github/workflows/update-studio-docs.yml b/.github/workflows/update-studio-docs.yml index fd258051e6..c303606d26 100644 --- a/.github/workflows/update-studio-docs.yml +++ b/.github/workflows/update-studio-docs.yml @@ -242,74 +242,7 @@ jobs: echo "$AI_RESPONSE" > .tmp/final-notes.txt else echo "⚠️ AI unavailable - generating concise user-friendly summaries from raw notes" - - python3 <<'PYTHON_EOF' - import os - import re - - raw = os.environ.get("RAW_NOTES", "") - lines = raw.splitlines() - - output = [] - seen = set() - - def clean_line(text: str) -> str: - text = text.strip() - if not text: - return "" - - # Drop markdown headers/changelog lines. - if re.match(r"^#+\s", text, flags=re.I): - return "" - if re.match(r"^\*\*?\s*full\s+changelog", text, flags=re.I): - return "" - if re.match(r"^full\s+changelog", text, flags=re.I): - return "" - - text = re.sub(r"^[\s\-*•]+", "", text) - text = re.sub(r"\s+by\s+@?[a-zA-Z0-9_-]+\s+in\s+https?://\S+", "", text) - text = re.sub(r"\s+by\s+@?[a-zA-Z0-9_-]+\s*$", "", text) - text = re.sub(r"@([a-zA-Z0-9_-]+)", "", text) - text = re.sub(r"\s*\([^)]*#\d+\)\s*$", "", text) - text = re.sub(r"\s+#\d+\s*$", "", text) - text = re.sub(r"\s+", " ", text).strip(" .:-") - - if len(text) < 8: - return "" - - # Make user-friendly short title + summary when possible. - if ":" in text: - left, right = [p.strip() for p in text.split(":", 1)] - left = left[:40].rstrip(" .") - right_words = right.split() - right = " ".join(right_words[:14]).rstrip(" .") - text = f"{left}: {right}" if right else left - else: - words = text.split() - if len(words) > 16: - text = " ".join(words[:16]).rstrip(" .") - - return text - - for line in lines: - cleaned = clean_line(line) - if not cleaned: - continue - - # Normalize casing and deduplicate. - cleaned = cleaned[0].upper() + cleaned[1:] if cleaned else cleaned - key = cleaned.lower() - if key in seen: - continue - seen.add(key) - - output.append(f"* {cleaned}") - if len(output) >= 8: - break - - with open('.tmp/final-notes.txt', 'w', encoding='utf-8') as f: - f.write("\n".join(output)) - PYTHON_EOF + python3 .github/scripts/format-studio-release-notes.py fi # Normalize bullets to "* " even if AI returns "- ". @@ -454,137 +387,8 @@ jobs: exit 0 fi - # Use Python for smart version range handling - python3 <<'PYTHON_EOF' - import os - import re - from packaging.version import Version, InvalidVersion - - studio_ver = os.environ["STUDIO_VERSION"] - abp_ver = os.environ["ABP_VERSION"] - file_path = "docs/en/studio/version-mapping.md" + python3 .github/scripts/update-studio-version-mapping.py - try: - studio = Version(studio_ver) - except InvalidVersion: - print(f"❌ Invalid Studio version: {studio_ver}") - exit(1) - - with open(file_path, 'r') as f: - lines = f.readlines() - - # Find table start (skip SEO and headers) - table_start = 0 - table_end = 0 - for i, line in enumerate(lines): - if line.strip().startswith('|') and '**ABP Studio Version**' in line: - table_start = i - elif table_start > 0 and line.strip() and not line.strip().startswith('|'): - table_end = i - break - - if table_start == 0: - print("❌ Could not find version mapping table") - exit(1) - - # If no end found, table goes to end of file - if table_end == 0: - table_end = len(lines) - - # Extract sections - before_table = lines[:table_start] # Everything before table - table_header = lines[table_start:table_start+2] # Header + separator - data_rows = [l for l in lines[table_start+2:table_end] if l.strip().startswith('|')] # Data rows - after_table = lines[table_end:] # Everything after table - - new_rows = [] - handled = False - - def parse_version_range(version_str): - """Parse '2.1.5 - 2.1.9' or '2.1.5' into (start, end)""" - version_str = version_str.strip() - - if '–' in version_str or '-' in version_str: - # Handle both em-dash and hyphen - parts = re.split(r'\s*[–-]\s*', version_str) - if len(parts) == 2: - try: - return Version(parts[0].strip()), Version(parts[1].strip()) - except InvalidVersion: - return None, None - - try: - v = Version(version_str) - return v, v - except InvalidVersion: - return None, None - - def format_row(studio_range, abp_version): - """Format a table row with proper spacing""" - return f"| {studio_range:<22} | {abp_version:<27} |\n" - - # Process existing rows - for row in data_rows: - match = re.match(r'\|\s*(.+?)\s*\|\s*(.+?)\s*\|', row) - if not match: - continue - - existing_studio_range = match.group(1).strip() - existing_abp = match.group(2).strip() - - # Only consider rows with matching ABP version - if existing_abp != abp_ver: - new_rows.append(row) - continue - - start_ver, end_ver = parse_version_range(existing_studio_range) - - if start_ver is None or end_ver is None: - new_rows.append(row) - continue - - # Check if current studio version is in this range - if start_ver <= studio <= end_ver: - print(f"✅ Studio version {studio_ver} already covered in range {existing_studio_range}") - handled = True - new_rows.append(row) - - # Check if we should extend the range - elif end_ver < studio: - # Calculate if studio is the next logical version - # For patch versions: 2.1.9 -> 2.1.10 - # For minor versions: 2.1.9 -> 2.2.0 - - # Simple heuristic: if major.minor match and patch increments, extend range - if (start_ver.major == studio.major and - start_ver.minor == studio.minor and - studio.micro <= end_ver.micro + 5): # Allow small gaps - - new_range = f"{start_ver} - {studio}" - new_rows.append(format_row(new_range, abp_ver)) - print(f"✅ Extended range: {new_range}") - handled = True - else: - new_rows.append(row) - else: - new_rows.append(row) - - # If not handled, add new row at top of data - if not handled: - new_row = format_row(str(studio), abp_ver) - new_rows.insert(0, new_row) - print(f"✅ Added new mapping: {studio_ver} -> {abp_ver}") - - # Write updated file - preserve ALL content - with open(file_path, 'w') as f: - f.writelines(before_table) # SEO, title, intro text - f.writelines(table_header) # Table header - f.writelines(new_rows) # Updated data rows - f.writelines(after_table) # Content after table (preview section, etc.) - - print("MAPPING_UPDATED=true") - PYTHON_EOF - echo "MAPPING_UPDATED=true" >> $GITHUB_ENV echo "=== Updated version-mapping.md preview ===" From 51047e5394f0ae0679ce6bac6cc8d533f5c312af Mon Sep 17 00:00:00 2001 From: selmankoc Date: Thu, 11 Jun 2026 14:46:26 +0300 Subject: [PATCH 4/5] feat: restore workflow helper scripts for documentation updates This commit adds steps to the GitHub Actions workflow to restore and install helper scripts from the workflow commit. These scripts are essential for managing the documentation process, ensuring that the necessary tools are available for generating and updating release notes and version mappings. --- .github/workflows/update-studio-docs.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/update-studio-docs.yml b/.github/workflows/update-studio-docs.yml index c303606d26..9d92a17527 100644 --- a/.github/workflows/update-studio-docs.yml +++ b/.github/workflows/update-studio-docs.yml @@ -164,6 +164,24 @@ jobs: git checkout -B "$BRANCH" echo "BRANCH=$BRANCH" >> $GITHUB_ENV + # ------------------------------------------------- + # Restore helper scripts from the workflow commit. + # The docs target branch (e.g. rel-10.4) may not contain them yet. + # ------------------------------------------------- + - name: Checkout workflow scripts + uses: actions/checkout@v4 + with: + repository: ${{ github.repository }} + ref: ${{ github.sha }} + sparse-checkout: .github/scripts + path: _workflow_scripts + + - name: Install workflow scripts + run: | + mkdir -p .github/scripts + cp _workflow_scripts/.github/scripts/*.py .github/scripts/ + ls -la .github/scripts/ + # ------------------------------------------------- # Analyze existing release notes format # ------------------------------------------------- From a38e137e35ddc7c9eefc2c7fc49ff3bc2813d663 Mon Sep 17 00:00:00 2001 From: selmankoc Date: Wed, 17 Jun 2026 16:11:23 +0300 Subject: [PATCH 5/5] refactor: streamline workflow for installing helper scripts This commit updates the GitHub Actions workflow to install helper scripts directly from the repository, ensuring that the necessary scripts for managing documentation are available. The previous checkout step has been removed, and the installation process has been simplified to enhance clarity and maintainability. --- .github/workflows/update-studio-docs.yml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/.github/workflows/update-studio-docs.yml b/.github/workflows/update-studio-docs.yml index 9d92a17527..1a5f6492c3 100644 --- a/.github/workflows/update-studio-docs.yml +++ b/.github/workflows/update-studio-docs.yml @@ -165,21 +165,14 @@ jobs: echo "BRANCH=$BRANCH" >> $GITHUB_ENV # ------------------------------------------------- - # Restore helper scripts from the workflow commit. - # The docs target branch (e.g. rel-10.4) may not contain them yet. + # Install helper scripts (embedded; target branch may not have them). + # Source of truth: .github/scripts/*.py in this repository. # ------------------------------------------------- - - name: Checkout workflow scripts - uses: actions/checkout@v4 - with: - repository: ${{ github.repository }} - ref: ${{ github.sha }} - sparse-checkout: .github/scripts - path: _workflow_scripts - - name: Install workflow scripts run: | - mkdir -p .github/scripts - cp _workflow_scripts/.github/scripts/*.py .github/scripts/ + mkdir -p .github/scripts .tmp + echo "aW1wb3J0IG9zCmltcG9ydCByZQoKcmF3ID0gb3MuZW52aXJvbi5nZXQoIlJBV19OT1RFUyIsICIiKQpsaW5lcyA9IHJhdy5zcGxpdGxpbmVzKCkKCm91dHB1dCA9IFtdCnNlZW4gPSBzZXQoKQoKCmRlZiBjbGVhbl9saW5lKHRleHQ6IHN0cikgLT4gc3RyOgogICAgdGV4dCA9IHRleHQuc3RyaXAoKQogICAgaWYgbm90IHRleHQ6CiAgICAgICAgcmV0dXJuICIiCgogICAgIyBEcm9wIG1hcmtkb3duIGhlYWRlcnMvY2hhbmdlbG9nIGxpbmVzLgogICAgaWYgcmUubWF0Y2gociJeIytccyIsIHRleHQsIGZsYWdzPXJlLkkpOgogICAgICAgIHJldHVybiAiIgogICAgaWYgcmUubWF0Y2gociJeXCpcKj9ccypmdWxsXHMrY2hhbmdlbG9nIiwgdGV4dCwgZmxhZ3M9cmUuSSk6CiAgICAgICAgcmV0dXJuICIiCiAgICBpZiByZS5tYXRjaChyIl5mdWxsXHMrY2hhbmdlbG9nIiwgdGV4dCwgZmxhZ3M9cmUuSSk6CiAgICAgICAgcmV0dXJuICIiCgogICAgdGV4dCA9IHJlLnN1YihyIl5bXHNcLSrigKJdKyIsICIiLCB0ZXh0KQogICAgdGV4dCA9IHJlLnN1YihyIlxzK2J5XHMrQD9bYS16QS1aMC05Xy1dK1xzK2luXHMraHR0cHM/Oi8vXFMrIiwgIiIsIHRleHQpCiAgICB0ZXh0ID0gcmUuc3ViKHIiXHMrYnlccytAP1thLXpBLVowLTlfLV0rXHMqJCIsICIiLCB0ZXh0KQogICAgdGV4dCA9IHJlLnN1YihyIkAoW2EtekEtWjAtOV8tXSspIiwgIiIsIHRleHQpCiAgICB0ZXh0ID0gcmUuc3ViKHIiXHMqXChbXildKiNcZCtcKVxzKiQiLCAiIiwgdGV4dCkKICAgIHRleHQgPSByZS5zdWIociJccysjXGQrXHMqJCIsICIiLCB0ZXh0KQogICAgdGV4dCA9IHJlLnN1YihyIlxzKyIsICIgIiwgdGV4dCkuc3RyaXAoIiAuOi0iKQoKICAgIGlmIGxlbih0ZXh0KSA8IDg6CiAgICAgICAgcmV0dXJuICIiCgogICAgIyBNYWtlIHVzZXItZnJpZW5kbHkgc2hvcnQgdGl0bGUgKyBzdW1tYXJ5IHdoZW4gcG9zc2libGUuCiAgICBpZiAiOiIgaW4gdGV4dDoKICAgICAgICBsZWZ0LCByaWdodCA9IFtwLnN0cmlwKCkgZm9yIHAgaW4gdGV4dC5zcGxpdCgiOiIsIDEpXQogICAgICAgIGxlZnQgPSBsZWZ0Wzo0MF0ucnN0cmlwKCIgLiIpCiAgICAgICAgcmlnaHRfd29yZHMgPSByaWdodC5zcGxpdCgpCiAgICAgICAgcmlnaHQgPSAiICIuam9pbihyaWdodF93b3Jkc1s6MTRdKS5yc3RyaXAoIiAuIikKICAgICAgICB0ZXh0ID0gZiJ7bGVmdH06IHtyaWdodH0iIGlmIHJpZ2h0IGVsc2UgbGVmdAogICAgZWxzZToKICAgICAgICB3b3JkcyA9IHRleHQuc3BsaXQoKQogICAgICAgIGlmIGxlbih3b3JkcykgPiAxNjoKICAgICAgICAgICAgdGV4dCA9ICIgIi5qb2luKHdvcmRzWzoxNl0pLnJzdHJpcCgiIC4iKQoKICAgIHJldHVybiB0ZXh0CgoKZm9yIGxpbmUgaW4gbGluZXM6CiAgICBjbGVhbmVkID0gY2xlYW5fbGluZShsaW5lKQogICAgaWYgbm90IGNsZWFuZWQ6CiAgICAgICAgY29udGludWUKCiAgICAjIE5vcm1hbGl6ZSBjYXNpbmcgYW5kIGRlZHVwbGljYXRlLgogICAgY2xlYW5lZCA9IGNsZWFuZWRbMF0udXBwZXIoKSArIGNsZWFuZWRbMTpdIGlmIGNsZWFuZWQgZWxzZSBjbGVhbmVkCiAgICBrZXkgPSBjbGVhbmVkLmxvd2VyKCkKICAgIGlmIGtleSBpbiBzZWVuOgogICAgICAgIGNvbnRpbnVlCiAgICBzZWVuLmFkZChrZXkpCgogICAgb3V0cHV0LmFwcGVuZChmIioge2NsZWFuZWR9IikKICAgIGlmIGxlbihvdXRwdXQpID49IDg6CiAgICAgICAgYnJlYWsKCm9zLm1ha2VkaXJzKCIudG1wIiwgZXhpc3Rfb2s9VHJ1ZSkKd2l0aCBvcGVuKCIudG1wL2ZpbmFsLW5vdGVzLnR4dCIsICJ3IiwgZW5jb2Rpbmc9InV0Zi04IikgYXMgZjoKICAgIGYud3JpdGUoIlxuIi5qb2luKG91dHB1dCkpCg==" | base64 -d > .github/scripts/format-studio-release-notes.py + echo "aW1wb3J0IG9zCmltcG9ydCByZQpmcm9tIHBhY2thZ2luZy52ZXJzaW9uIGltcG9ydCBWZXJzaW9uLCBJbnZhbGlkVmVyc2lvbgoKc3R1ZGlvX3ZlciA9IG9zLmVudmlyb25bIlNUVURJT19WRVJTSU9OIl0KYWJwX3ZlciA9IG9zLmVudmlyb25bIkFCUF9WRVJTSU9OIl0KZmlsZV9wYXRoID0gImRvY3MvZW4vc3R1ZGlvL3ZlcnNpb24tbWFwcGluZy5tZCIKCnRyeToKICAgIHN0dWRpbyA9IFZlcnNpb24oc3R1ZGlvX3ZlcikKZXhjZXB0IEludmFsaWRWZXJzaW9uOgogICAgcHJpbnQoZiLinYwgSW52YWxpZCBTdHVkaW8gdmVyc2lvbjoge3N0dWRpb192ZXJ9IikKICAgIHJhaXNlIFN5c3RlbUV4aXQoMSkKCndpdGggb3BlbihmaWxlX3BhdGgsICJyIikgYXMgZjoKICAgIGxpbmVzID0gZi5yZWFkbGluZXMoKQoKIyBGaW5kIHRhYmxlIHN0YXJ0IChza2lwIFNFTyBhbmQgaGVhZGVycykKdGFibGVfc3RhcnQgPSAwCnRhYmxlX2VuZCA9IDAKZm9yIGksIGxpbmUgaW4gZW51bWVyYXRlKGxpbmVzKToKICAgIGlmIGxpbmUuc3RyaXAoKS5zdGFydHN3aXRoKCJ8IikgYW5kICIqKkFCUCBTdHVkaW8gVmVyc2lvbioqIiBpbiBsaW5lOgogICAgICAgIHRhYmxlX3N0YXJ0ID0gaQogICAgZWxpZiB0YWJsZV9zdGFydCA+IDAgYW5kIGxpbmUuc3RyaXAoKSBhbmQgbm90IGxpbmUuc3RyaXAoKS5zdGFydHN3aXRoKCJ8Iik6CiAgICAgICAgdGFibGVfZW5kID0gaQogICAgICAgIGJyZWFrCgppZiB0YWJsZV9zdGFydCA9PSAwOgogICAgcHJpbnQoIuKdjCBDb3VsZCBub3QgZmluZCB2ZXJzaW9uIG1hcHBpbmcgdGFibGUiKQogICAgcmFpc2UgU3lzdGVtRXhpdCgxKQoKIyBJZiBubyBlbmQgZm91bmQsIHRhYmxlIGdvZXMgdG8gZW5kIG9mIGZpbGUKaWYgdGFibGVfZW5kID09IDA6CiAgICB0YWJsZV9lbmQgPSBsZW4obGluZXMpCgojIEV4dHJhY3Qgc2VjdGlvbnMKYmVmb3JlX3RhYmxlID0gbGluZXNbOnRhYmxlX3N0YXJ0XQp0YWJsZV9oZWFkZXIgPSBsaW5lc1t0YWJsZV9zdGFydCA6IHRhYmxlX3N0YXJ0ICsgMl0KZGF0YV9yb3dzID0gW2wgZm9yIGwgaW4gbGluZXNbdGFibGVfc3RhcnQgKyAyIDogdGFibGVfZW5kXSBpZiBsLnN0cmlwKCkuc3RhcnRzd2l0aCgifCIpXQphZnRlcl90YWJsZSA9IGxpbmVzW3RhYmxlX2VuZDpdCgpuZXdfcm93cyA9IFtdCmhhbmRsZWQgPSBGYWxzZQoKCmRlZiBwYXJzZV92ZXJzaW9uX3JhbmdlKHZlcnNpb25fc3RyKToKICAgICIiIlBhcnNlICcyLjEuNSAtIDIuMS45JyBvciAnMi4xLjUnIGludG8gKHN0YXJ0LCBlbmQpIiIiCiAgICB2ZXJzaW9uX3N0ciA9IHZlcnNpb25fc3RyLnN0cmlwKCkKCiAgICBpZiAi4oCTIiBpbiB2ZXJzaW9uX3N0ciBvciAiLSIgaW4gdmVyc2lvbl9zdHI6CiAgICAgICAgcGFydHMgPSByZS5zcGxpdChyIlxzKlvigJMtXVxzKiIsIHZlcnNpb25fc3RyKQogICAgICAgIGlmIGxlbihwYXJ0cykgPT0gMjoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgcmV0dXJuIFZlcnNpb24ocGFydHNbMF0uc3RyaXAoKSksIFZlcnNpb24ocGFydHNbMV0uc3RyaXAoKSkKICAgICAgICAgICAgZXhjZXB0IEludmFsaWRWZXJzaW9uOgogICAgICAgICAgICAgICAgcmV0dXJuIE5vbmUsIE5vbmUKCiAgICB0cnk6CiAgICAgICAgdiA9IFZlcnNpb24odmVyc2lvbl9zdHIpCiAgICAgICAgcmV0dXJuIHYsIHYKICAgIGV4Y2VwdCBJbnZhbGlkVmVyc2lvbjoKICAgICAgICByZXR1cm4gTm9uZSwgTm9uZQoKCmRlZiBmb3JtYXRfcm93KHN0dWRpb19yYW5nZSwgYWJwX3ZlcnNpb24pOgogICAgIiIiRm9ybWF0IGEgdGFibGUgcm93IHdpdGggcHJvcGVyIHNwYWNpbmciIiIKICAgIHJldHVybiBmInwge3N0dWRpb19yYW5nZTo8MjJ9IHwge2FicF92ZXJzaW9uOjwyN30gfFxuIgoKCiMgUHJvY2VzcyBleGlzdGluZyByb3dzCmZvciByb3cgaW4gZGF0YV9yb3dzOgogICAgbWF0Y2ggPSByZS5tYXRjaChyIlx8XHMqKC4rPylccypcfFxzKiguKz8pXHMqXHwiLCByb3cpCiAgICBpZiBub3QgbWF0Y2g6CiAgICAgICAgY29udGludWUKCiAgICBleGlzdGluZ19zdHVkaW9fcmFuZ2UgPSBtYXRjaC5ncm91cCgxKS5zdHJpcCgpCiAgICBleGlzdGluZ19hYnAgPSBtYXRjaC5ncm91cCgyKS5zdHJpcCgpCgogICAgaWYgZXhpc3RpbmdfYWJwICE9IGFicF92ZXI6CiAgICAgICAgbmV3X3Jvd3MuYXBwZW5kKHJvdykKICAgICAgICBjb250aW51ZQoKICAgIHN0YXJ0X3ZlciwgZW5kX3ZlciA9IHBhcnNlX3ZlcnNpb25fcmFuZ2UoZXhpc3Rpbmdfc3R1ZGlvX3JhbmdlKQoKICAgIGlmIHN0YXJ0X3ZlciBpcyBOb25lIG9yIGVuZF92ZXIgaXMgTm9uZToKICAgICAgICBuZXdfcm93cy5hcHBlbmQocm93KQogICAgICAgIGNvbnRpbnVlCgogICAgaWYgc3RhcnRfdmVyIDw9IHN0dWRpbyA8PSBlbmRfdmVyOgogICAgICAgIHByaW50KGYi4pyFIFN0dWRpbyB2ZXJzaW9uIHtzdHVkaW9fdmVyfSBhbHJlYWR5IGNvdmVyZWQgaW4gcmFuZ2Uge2V4aXN0aW5nX3N0dWRpb19yYW5nZX0iKQogICAgICAgIGhhbmRsZWQgPSBUcnVlCiAgICAgICAgbmV3X3Jvd3MuYXBwZW5kKHJvdykKICAgIGVsaWYgZW5kX3ZlciA8IHN0dWRpbzoKICAgICAgICBpZiAoCiAgICAgICAgICAgIHN0YXJ0X3Zlci5tYWpvciA9PSBzdHVkaW8ubWFqb3IKICAgICAgICAgICAgYW5kIHN0YXJ0X3Zlci5taW5vciA9PSBzdHVkaW8ubWlub3IKICAgICAgICAgICAgYW5kIHN0dWRpby5taWNybyA8PSBlbmRfdmVyLm1pY3JvICsgNQogICAgICAgICk6CiAgICAgICAgICAgIG5ld19yYW5nZSA9IGYie3N0YXJ0X3Zlcn0gLSB7c3R1ZGlvfSIKICAgICAgICAgICAgbmV3X3Jvd3MuYXBwZW5kKGZvcm1hdF9yb3cobmV3X3JhbmdlLCBhYnBfdmVyKSkKICAgICAgICAgICAgcHJpbnQoZiLinIUgRXh0ZW5kZWQgcmFuZ2U6IHtuZXdfcmFuZ2V9IikKICAgICAgICAgICAgaGFuZGxlZCA9IFRydWUKICAgICAgICBlbHNlOgogICAgICAgICAgICBuZXdfcm93cy5hcHBlbmQocm93KQogICAgZWxzZToKICAgICAgICBuZXdfcm93cy5hcHBlbmQocm93KQoKaWYgbm90IGhhbmRsZWQ6CiAgICBuZXdfcm93ID0gZm9ybWF0X3JvdyhzdHIoc3R1ZGlvKSwgYWJwX3ZlcikKICAgIG5ld19yb3dzLmluc2VydCgwLCBuZXdfcm93KQogICAgcHJpbnQoZiLinIUgQWRkZWQgbmV3IG1hcHBpbmc6IHtzdHVkaW9fdmVyfSAtPiB7YWJwX3Zlcn0iKQoKd2l0aCBvcGVuKGZpbGVfcGF0aCwgInciKSBhcyBmOgogICAgZi53cml0ZWxpbmVzKGJlZm9yZV90YWJsZSkKICAgIGYud3JpdGVsaW5lcyh0YWJsZV9oZWFkZXIpCiAgICBmLndyaXRlbGluZXMobmV3X3Jvd3MpCiAgICBmLndyaXRlbGluZXMoYWZ0ZXJfdGFibGUpCg==" | base64 -d > .github/scripts/update-studio-version-mapping.py ls -la .github/scripts/ # -------------------------------------------------