mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-20 20:39:10 +00:00
190 lines
4.8 KiB
Python
Generated
190 lines
4.8 KiB
Python
Generated
#!/usr/bin/env python3
|
|
"""Cloudflare R2 client utilities for BrowserOS build system
|
|
|
|
This module provides shared R2 (S3-compatible) operations used by both
|
|
upload and download modules.
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Dict, Optional
|
|
|
|
from ...common.env import EnvConfig
|
|
from ...common.utils import log_info, log_error, log_success, log_warning
|
|
|
|
# Try to import boto3 for R2 (S3-compatible)
|
|
try:
|
|
import boto3
|
|
from botocore.config import Config
|
|
|
|
BOTO3_AVAILABLE = True
|
|
except ImportError:
|
|
BOTO3_AVAILABLE = False
|
|
|
|
|
|
def get_r2_client(env: Optional[EnvConfig] = None):
|
|
"""Create boto3 S3 client configured for R2
|
|
|
|
Args:
|
|
env: Optional EnvConfig instance. If not provided, creates a new one.
|
|
|
|
Returns:
|
|
boto3 S3 client configured for R2, or None if not available
|
|
"""
|
|
if not BOTO3_AVAILABLE:
|
|
log_error("boto3 not installed - run: pip install boto3")
|
|
return None
|
|
|
|
if env is None:
|
|
env = EnvConfig()
|
|
|
|
if not env.has_r2_config():
|
|
log_error("R2 configuration not set")
|
|
return None
|
|
|
|
return boto3.client(
|
|
"s3",
|
|
endpoint_url=env.r2_endpoint_url,
|
|
aws_access_key_id=env.r2_access_key_id,
|
|
aws_secret_access_key=env.r2_secret_access_key,
|
|
config=Config(
|
|
signature_version="s3v4",
|
|
retries={"max_attempts": 3, "mode": "standard"},
|
|
),
|
|
)
|
|
|
|
|
|
def upload_file_to_r2(
|
|
client,
|
|
local_path: Path,
|
|
r2_key: str,
|
|
bucket: str,
|
|
) -> bool:
|
|
"""Upload a single file to R2
|
|
|
|
Args:
|
|
client: boto3 S3 client
|
|
local_path: Path to local file
|
|
r2_key: Key (path) in R2 bucket
|
|
bucket: R2 bucket name
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
try:
|
|
log_info(f"Uploading {local_path.name}...")
|
|
client.upload_file(str(local_path), bucket, r2_key)
|
|
log_success(f"Uploaded: {r2_key}")
|
|
return True
|
|
except Exception as e:
|
|
log_error(f"Failed to upload {local_path.name}: {e}")
|
|
return False
|
|
|
|
|
|
def download_file_from_r2(
|
|
client,
|
|
r2_key: str,
|
|
dest_path: Path,
|
|
bucket: str,
|
|
) -> bool:
|
|
"""Download a single file from R2
|
|
|
|
Args:
|
|
client: boto3 S3 client
|
|
r2_key: Key (path) in R2 bucket
|
|
dest_path: Local destination path
|
|
bucket: R2 bucket name
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
try:
|
|
log_info(f"Downloading {r2_key}...")
|
|
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
client.download_file(bucket, r2_key, str(dest_path))
|
|
log_success(f"Downloaded: {dest_path.name}")
|
|
return True
|
|
except Exception as e:
|
|
log_error(f"Failed to download {r2_key}: {e}")
|
|
return False
|
|
|
|
|
|
def download_from_r2(
|
|
r2_key: str,
|
|
dest_path: Path,
|
|
bucket: Optional[str] = None,
|
|
env: Optional[EnvConfig] = None,
|
|
) -> bool:
|
|
"""Download a file from R2 (convenience wrapper)
|
|
|
|
Args:
|
|
r2_key: Key (path) in R2 bucket
|
|
dest_path: Local destination path
|
|
bucket: Optional bucket name (uses default from env if not specified)
|
|
env: Optional EnvConfig instance. If not provided, creates a new one.
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
if not BOTO3_AVAILABLE:
|
|
log_error("boto3 not installed")
|
|
return False
|
|
|
|
if env is None:
|
|
env = EnvConfig()
|
|
|
|
if not env.has_r2_config():
|
|
log_error("R2 configuration not set")
|
|
return False
|
|
|
|
client = get_r2_client(env)
|
|
if not client:
|
|
return False
|
|
|
|
bucket = bucket or env.r2_bucket
|
|
return download_file_from_r2(client, r2_key, dest_path, bucket)
|
|
|
|
|
|
def get_release_json(
|
|
version: str,
|
|
platform: str,
|
|
env: Optional[EnvConfig] = None,
|
|
) -> Optional[Dict]:
|
|
"""Fetch release.json for a specific version and platform from R2
|
|
|
|
Args:
|
|
version: Semantic version (e.g., "0.31.0")
|
|
platform: Platform name (macos, win, linux)
|
|
env: Optional EnvConfig instance. If not provided, creates a new one.
|
|
|
|
Returns:
|
|
Parsed release.json dict, or None if not found
|
|
"""
|
|
if not BOTO3_AVAILABLE:
|
|
log_error("boto3 not installed")
|
|
return None
|
|
|
|
if env is None:
|
|
env = EnvConfig()
|
|
|
|
if not env.has_r2_config():
|
|
log_error("R2 configuration not set")
|
|
return None
|
|
|
|
client = get_r2_client(env)
|
|
if not client:
|
|
return None
|
|
|
|
r2_key = f"releases/{version}/{platform}/release.json"
|
|
|
|
try:
|
|
response = client.get_object(Bucket=env.r2_bucket, Key=r2_key)
|
|
content = response["Body"].read().decode("utf-8")
|
|
return json.loads(content)
|
|
except client.exceptions.NoSuchKey:
|
|
log_warning(f"release.json not found: {r2_key}")
|
|
return None
|
|
except Exception as e:
|
|
log_error(f"Failed to fetch release.json: {e}")
|
|
return None
|