gcs upload module

This commit is contained in:
Nikhil Sonti
2025-07-05 10:17:34 -07:00
parent 7cc38ee1fa
commit a97fef635c
4 changed files with 197 additions and 0 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@
**/logs
**/old-scripts
**/__pycache__/**
nxtscape-cli-access.json

View File

@@ -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
View 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

View File

@@ -2,3 +2,4 @@
click>=8.0.0
PyYAML>=5.4.1
requests>=2.25.1
google-cloud-storage>=2.10.0