mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2025-07-21 00:21:28 +03:00
gcs upload module
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
||||
**/logs
|
||||
**/old-scripts
|
||||
**/__pycache__/**
|
||||
nxtscape-cli-access.json
|
||||
|
||||
@@ -23,6 +23,7 @@ from modules.string_replaces import apply_string_replacements
|
||||
from modules.inject import inject_version
|
||||
from modules.configure import configure
|
||||
from modules.compile import build
|
||||
from modules.gcs import upload_package_artifacts, upload_signed_artifacts
|
||||
|
||||
# Platform-specific imports
|
||||
if IS_MACOS:
|
||||
@@ -76,6 +77,7 @@ def build_main(
|
||||
chromium_src_dir: Optional[Path] = None,
|
||||
slack_notifications: bool = False,
|
||||
patch_interactive: bool = False,
|
||||
upload_gcs: bool = True, # Default to uploading to GCS
|
||||
):
|
||||
"""Main build orchestration"""
|
||||
log_info("🚀 Nxtscape Build System")
|
||||
@@ -270,6 +272,10 @@ def build_main(
|
||||
package(ctx)
|
||||
if slack_notifications:
|
||||
notify_build_step(f"[{ctx.architecture}] Completed DMG creation")
|
||||
|
||||
# Upload to GCS after packaging
|
||||
if upload_gcs:
|
||||
upload_package_artifacts(ctx)
|
||||
|
||||
built_contexts.append(ctx)
|
||||
|
||||
@@ -327,6 +333,15 @@ def build_main(
|
||||
package_universal(built_contexts)
|
||||
if slack_notifications:
|
||||
notify_build_step("[Universal] Completed DMG package creation")
|
||||
|
||||
# Upload universal package to GCS
|
||||
if upload_gcs:
|
||||
# Use the first context with universal architecture override
|
||||
universal_ctx = built_contexts[0]
|
||||
original_arch = universal_ctx.architecture
|
||||
universal_ctx.architecture = "universal"
|
||||
upload_package_artifacts(universal_ctx)
|
||||
universal_ctx.architecture = original_arch
|
||||
|
||||
# Summary
|
||||
elapsed = time.time() - start_time
|
||||
@@ -426,6 +441,12 @@ def build_main(
|
||||
default=False,
|
||||
help="Ask for confirmation before applying each patch",
|
||||
)
|
||||
@click.option(
|
||||
"--no-gcs-upload",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Skip uploading artifacts to Google Cloud Storage",
|
||||
)
|
||||
def main(
|
||||
config,
|
||||
clean,
|
||||
@@ -442,6 +463,7 @@ def main(
|
||||
add_replace,
|
||||
string_replace,
|
||||
patch_interactive,
|
||||
no_gcs_upload,
|
||||
):
|
||||
"""Simple build system for Nxtscape Browser"""
|
||||
|
||||
@@ -532,6 +554,7 @@ def main(
|
||||
chromium_src_dir=chromium_src,
|
||||
slack_notifications=slack_notifications,
|
||||
patch_interactive=patch_interactive,
|
||||
upload_gcs=not no_gcs_upload, # Invert the flag
|
||||
)
|
||||
|
||||
|
||||
|
||||
172
build/modules/gcs.py
Normal file
172
build/modules/gcs.py
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Google Cloud Storage upload module for Nxtscape build artifacts
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
from context import BuildContext
|
||||
from utils import log_info, log_error, log_success, log_warning, IS_WINDOWS, IS_MACOS, join_paths
|
||||
|
||||
# Try to import google-cloud-storage
|
||||
try:
|
||||
from google.cloud import storage
|
||||
from google.oauth2 import service_account
|
||||
GCS_AVAILABLE = True
|
||||
except ImportError:
|
||||
GCS_AVAILABLE = False
|
||||
|
||||
# Service account file name
|
||||
SERVICE_ACCOUNT_FILE = "nxtscape-cli-access.json"
|
||||
|
||||
|
||||
def upload_to_gcs(ctx: BuildContext, file_paths: List[Path]) -> bool:
|
||||
"""Upload build artifacts to Google Cloud Storage"""
|
||||
if not GCS_AVAILABLE:
|
||||
log_warning("google-cloud-storage not installed. Skipping GCS upload.")
|
||||
log_info("Install with: pip install google-cloud-storage")
|
||||
return True # Not a fatal error
|
||||
|
||||
if not file_paths:
|
||||
log_info("No files to upload to GCS")
|
||||
return True
|
||||
|
||||
# Determine platform subdirectory
|
||||
if IS_WINDOWS:
|
||||
platform_dir = "win"
|
||||
elif IS_MACOS:
|
||||
platform_dir = "macos"
|
||||
else:
|
||||
platform_dir = "linux"
|
||||
|
||||
# Build GCS path: gs://nxtscape/resources/<version>/<platform>/
|
||||
bucket_name = "nxtscape"
|
||||
gcs_prefix = f"resources/{ctx.nxtscape_version}/{platform_dir}"
|
||||
|
||||
log_info(f"\n☁️ Uploading artifacts to gs://{bucket_name}/{gcs_prefix}/")
|
||||
|
||||
# Check for service account file
|
||||
service_account_path = join_paths(ctx.root_dir, SERVICE_ACCOUNT_FILE)
|
||||
if not service_account_path.exists():
|
||||
log_error(f"Service account file not found: {SERVICE_ACCOUNT_FILE}")
|
||||
log_info(f"Please place the service account JSON file at: {service_account_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Initialize GCS client with service account
|
||||
credentials = service_account.Credentials.from_service_account_file(
|
||||
str(service_account_path)
|
||||
)
|
||||
client = storage.Client(credentials=credentials)
|
||||
bucket = client.bucket(bucket_name)
|
||||
|
||||
uploaded_files = []
|
||||
|
||||
for file_path in file_paths:
|
||||
if not file_path.exists():
|
||||
log_warning(f"File not found, skipping: {file_path}")
|
||||
continue
|
||||
|
||||
# Determine blob name (file name in GCS)
|
||||
blob_name = f"{gcs_prefix}/{file_path.name}"
|
||||
|
||||
try:
|
||||
blob = bucket.blob(blob_name)
|
||||
|
||||
log_info(f"📤 Uploading {file_path.name}...")
|
||||
blob.upload_from_filename(str(file_path))
|
||||
|
||||
# Make the blob publicly readable
|
||||
blob.make_public()
|
||||
|
||||
public_url = f"https://storage.googleapis.com/{bucket_name}/{blob_name}"
|
||||
uploaded_files.append(public_url)
|
||||
log_success(f"✓ Uploaded: {public_url}")
|
||||
|
||||
except Exception as e:
|
||||
log_error(f"Failed to upload {file_path.name}: {e}")
|
||||
return False
|
||||
|
||||
if uploaded_files:
|
||||
log_success(f"\n☁️ Successfully uploaded {len(uploaded_files)} file(s) to GCS")
|
||||
log_info("\nPublic URLs:")
|
||||
for url in uploaded_files:
|
||||
log_info(f" {url}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
log_error(f"GCS upload failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def upload_package_artifacts(ctx: BuildContext) -> bool:
|
||||
"""Upload package artifacts (DMG, ZIP, EXE) to GCS"""
|
||||
log_info("\n☁️ Preparing to upload package artifacts to GCS...")
|
||||
|
||||
artifacts = []
|
||||
|
||||
if IS_MACOS:
|
||||
# Look for DMG files
|
||||
dmg_dir = ctx.root_dir / "dmg"
|
||||
if dmg_dir.exists():
|
||||
artifacts.extend(dmg_dir.glob("*.dmg"))
|
||||
|
||||
elif IS_WINDOWS:
|
||||
# Look for installer and ZIP files
|
||||
dist_dir = ctx.root_dir / "dist"
|
||||
if dist_dir.exists():
|
||||
artifacts.extend(dist_dir.glob("*.exe"))
|
||||
artifacts.extend(dist_dir.glob("*.zip"))
|
||||
|
||||
if not artifacts:
|
||||
log_info("No package artifacts found to upload")
|
||||
return True
|
||||
|
||||
log_info(f"Found {len(artifacts)} artifact(s) to upload:")
|
||||
for artifact in artifacts:
|
||||
log_info(f" - {artifact.name}")
|
||||
|
||||
return upload_to_gcs(ctx, artifacts)
|
||||
|
||||
|
||||
def upload_signed_artifacts(ctx: BuildContext) -> bool:
|
||||
"""Upload signed artifacts to GCS"""
|
||||
# For now, this is the same as package artifacts
|
||||
# Can be extended in the future for specific signed artifacts
|
||||
return upload_package_artifacts(ctx)
|
||||
|
||||
|
||||
def download_from_gcs(bucket_name: str, source_path: str, dest_path: Path, ctx: Optional[BuildContext] = None) -> bool:
|
||||
"""Download a file from GCS (utility function)"""
|
||||
if not GCS_AVAILABLE:
|
||||
log_error("google-cloud-storage not installed")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Try to use service account if available
|
||||
client = None
|
||||
if ctx:
|
||||
service_account_path = join_paths(ctx.root_dir, SERVICE_ACCOUNT_FILE)
|
||||
if service_account_path.exists():
|
||||
credentials = service_account.Credentials.from_service_account_file(
|
||||
str(service_account_path)
|
||||
)
|
||||
client = storage.Client(credentials=credentials)
|
||||
|
||||
# Fall back to anonymous client for public buckets
|
||||
if not client:
|
||||
client = storage.Client.create_anonymous_client()
|
||||
|
||||
bucket = client.bucket(bucket_name)
|
||||
blob = bucket.blob(source_path)
|
||||
|
||||
log_info(f"📥 Downloading gs://{bucket_name}/{source_path}...")
|
||||
blob.download_to_filename(str(dest_path))
|
||||
log_success(f"Downloaded to: {dest_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
log_error(f"Failed to download from GCS: {e}")
|
||||
return False
|
||||
@@ -2,3 +2,4 @@
|
||||
click>=8.0.0
|
||||
PyYAML>=5.4.1
|
||||
requests>=2.25.1
|
||||
google-cloud-storage>=2.10.0
|
||||
|
||||
Reference in New Issue
Block a user