Update component version (#1382)

* Add download_github_runs.py

* Update esp-ml307 version

* Update esp-sr to 2.2.0
This commit is contained in:
Xiaoxia
2025-11-04 18:40:54 +08:00
committed by GitHub
parent 0e51c4c94c
commit c6815c7ad5
2 changed files with 266 additions and 2 deletions

View File

@@ -0,0 +1,264 @@
#!/usr/bin/env python3
"""
Download GitHub Actions artifacts and rename them with version numbers.
Usage:
python download_github_runs.py 2.0.4 https://github.com/78/xiaozhi-esp32/actions/runs/18866246016
"""
import argparse
import os
import re
import sys
import zipfile
from pathlib import Path
from urllib.parse import urlparse
import requests
from dotenv import load_dotenv
def parse_github_run_url(url: str) -> tuple[str, str, str]:
"""
Parse GitHub Actions run URL to extract owner, repo, and run_id.
Args:
url: GitHub Actions run URL
Returns:
Tuple of (owner, repo, run_id)
"""
# Example: https://github.com/78/xiaozhi-esp32/actions/runs/18866246016
pattern = r'github\.com/([^/]+)/([^/]+)/actions/runs/(\d+)'
match = re.search(pattern, url)
if not match:
raise ValueError(f"Invalid GitHub Actions URL: {url}")
owner, repo, run_id = match.groups()
return owner, repo, run_id
def get_artifacts(owner: str, repo: str, run_id: str, token: str) -> list[dict]:
"""
Get all artifacts for a specific workflow run (with pagination support).
Args:
owner: Repository owner
repo: Repository name
run_id: Workflow run ID
token: GitHub personal access token
Returns:
List of artifact dictionaries
"""
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28"
}
all_artifacts = []
page = 1
per_page = 100 # Maximum allowed by GitHub API
while True:
url = f"https://api.github.com/repos/{owner}/{repo}/actions/runs/{run_id}/artifacts"
params = {
"page": page,
"per_page": per_page
}
response = requests.get(url, headers=headers, params=params)
response.raise_for_status()
data = response.json()
artifacts = data.get("artifacts", [])
if not artifacts:
break
all_artifacts.extend(artifacts)
# Check if there are more pages
total_count = data.get("total_count", 0)
if len(all_artifacts) >= total_count:
break
page += 1
return all_artifacts
def download_artifact(artifact_url: str, token: str, output_path: Path) -> None:
"""
Download an artifact from GitHub.
Args:
artifact_url: Artifact download URL
token: GitHub personal access token
output_path: Path to save the downloaded artifact
"""
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28"
}
response = requests.get(artifact_url, headers=headers, stream=True)
response.raise_for_status()
# Create parent directory if it doesn't exist
output_path.parent.mkdir(parents=True, exist_ok=True)
# Download the file
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
def rename_artifact(original_name: str, version: str) -> str:
"""
Rename artifact according to the specified rules.
Rules:
- Remove "xiaozhi_" prefix
- Remove hash suffix (underscore followed by hex string)
- Add version prefix (e.g., "v2.0.4_")
- Change extension to .zip
Example:
xiaozhi_atk-dnesp32s3-box0_43ef2f4e7f0957dc62ec7d628ac2819d226127b8.bin
-> v2.0.4_atk-dnesp32s3-box0.zip
Args:
original_name: Original artifact name
version: Version string (e.g., "2.0.4")
Returns:
New filename
"""
# Remove "xiaozhi_" prefix
name = original_name
if name.startswith("xiaozhi_"):
name = name[len("xiaozhi_"):]
# Remove extension
name_without_ext = os.path.splitext(name)[0]
# Remove hash suffix (pattern: underscore followed by 40+ hex characters)
# This matches Git commit hashes and similar identifiers
name_without_hash = re.sub(r'_[a-f0-9]{40,}$', '', name_without_ext)
# Add version prefix and .zip extension
new_name = f"v{version}_{name_without_hash}.zip"
return new_name
def main():
"""Main function to download and rename GitHub Actions artifacts."""
parser = argparse.ArgumentParser(
description="Download GitHub Actions artifacts and rename them with version numbers."
)
parser.add_argument(
"version",
help="Version number (e.g., 2.0.4)"
)
parser.add_argument(
"url",
help="GitHub Actions run URL (e.g., https://github.com/owner/repo/actions/runs/12345)"
)
parser.add_argument(
"--output-dir",
default="../releases",
help="Output directory for downloaded artifacts (default: ../releases)"
)
args = parser.parse_args()
# Load GitHub token from .env file
load_dotenv()
github_token = os.getenv("GITHUB_TOKEN")
if not github_token:
print("Error: GITHUB_TOKEN not found in environment variables.", file=sys.stderr)
print("Please create a .env file with GITHUB_TOKEN=your_token_here", file=sys.stderr)
sys.exit(1)
try:
# Parse the GitHub URL
owner, repo, run_id = parse_github_run_url(args.url)
print(f"Repository: {owner}/{repo}")
print(f"Run ID: {run_id}")
print(f"Version: {args.version}")
print()
# Get artifacts
print("Fetching artifacts...")
artifacts = get_artifacts(owner, repo, run_id, github_token)
if not artifacts:
print("No artifacts found for this run.")
return
print(f"Found {len(artifacts)} artifact(s):")
for artifact in artifacts:
print(f" - {artifact['name']}")
print()
# Create output directory
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
# Download and rename each artifact
downloaded_count = 0
skipped_count = 0
for artifact in artifacts:
original_name = artifact['name']
new_name = rename_artifact(original_name, args.version)
final_path = output_dir / new_name
# Check if file already exists
if final_path.exists():
print(f"Skipping (already exists): {original_name}")
print(f" -> {new_name}")
print(f" File: {final_path}")
print()
skipped_count += 1
continue
print(f"Downloading: {original_name}")
print(f" -> {new_name}")
# Download to temporary path first
temp_path = output_dir / f"{original_name}.zip"
download_artifact(
artifact['archive_download_url'],
github_token,
temp_path
)
# Rename to final name
temp_path.rename(final_path)
print(f" Saved to: {final_path}")
print()
downloaded_count += 1
print(f"Summary:")
print(f" Downloaded: {downloaded_count} artifact(s)")
print(f" Skipped: {skipped_count} artifact(s)")
print(f" Total: {len(artifacts)} artifact(s)")
print(f" Output directory: {output_dir.absolute()}")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()