diff --git a/cicd_tool/main.py b/cicd_tool/main.py index cf6ced4..38f2bc8 100644 --- a/cicd_tool/main.py +++ b/cicd_tool/main.py @@ -4,7 +4,7 @@ import json import os from datetime import datetime, timezone from pathlib import Path -from typing import Any, Iterable, List, Optional +from typing import Any, Iterable, List, NamedTuple, Optional from urllib.parse import urlparse from minio import Minio @@ -20,6 +20,11 @@ class ConfigurationError(RuntimeError): """Raised when required configuration values are missing or invalid.""" +class PublishTarget(NamedTuple): + bucket_name: str + app_dir: str + + def _resolve_version(version_arg: Optional[str]) -> str: """Resolve the version to publish, falling back to tauri.conf.json.""" if version_arg and version_arg.lower() not in {"auto"}: @@ -259,6 +264,71 @@ def _build_latest_payload( } +def _parse_publish_targets(primary_bucket: str, primary_app_dir: str) -> list[PublishTarget]: + targets = [PublishTarget(primary_bucket, primary_app_dir)] + raw_legacy_targets = os.environ.get("LEGACY_UPDATE_TARGETS", "gitlite:gitlite") + + for raw_target in raw_legacy_targets.split(","): + value = raw_target.strip() + if not value: + continue + + if ":" in value: + bucket_name, app_dir = value.split(":", 1) + else: + bucket_name = value + app_dir = value + + target = PublishTarget(bucket_name.strip().lower(), app_dir.strip("/")) + if target.bucket_name and target not in targets: + targets.append(target) + + return targets + + +def _publish_target( + client: Minio, + target: PublishTarget, + version: str, + platform: str, + artifacts: Iterable[Path], + primary_artifact: Path, + signature: str, + artifact_base_url: str, +) -> None: + _ensure_bucket(client, target.bucket_name) + existing_latest_json = _read_existing_latest_json(client, target.bucket_name) + latest_payload = _build_latest_payload( + version=version, + platform=platform, + primary_artifact=primary_artifact, + signature=signature, + artifact_base_url=artifact_base_url, + app_dir=target.app_dir, + existing_payload=existing_latest_json, + ) + latest_json = json.dumps(latest_payload, separators=(",", ":")) + + for artifact in artifacts: + object_segments = [part for part in (version, artifact.name) if part] + object_name = "/".join(object_segments) + client.fput_object(target.bucket_name, object_name, str(artifact)) + print(f"Uploaded {artifact.name} -> s3://{target.bucket_name}/{object_name}") + + latest_bytes = latest_json.encode("utf-8") + client.put_object( + bucket_name=target.bucket_name, + object_name="latest.json", + data=io.BytesIO(latest_bytes), + length=len(latest_bytes), + content_type="application/json", + ) + print( + f"Uploaded latest.json for {platform} -> " + f"s3://{target.bucket_name}/latest.json" + ) + + def main(version: Optional[str] = None, app_dir: Optional[str] = None) -> None: resolved_version = _resolve_version(version) artifact_base_url = os.environ.get("ARTIFACT_BASE_URL", "") @@ -269,6 +339,7 @@ def main(version: Optional[str] = None, app_dir: Optional[str] = None) -> None: # S3_BUCKET variable is cased (e.g. "Gitty" -> "gitty"). bucket_name = (os.environ.get("S3_BUCKET") or "gitty").lower() bucket_prefix = (app_dir or os.environ.get("APP_DIR") or "gitty").strip("/") + publish_targets = _parse_publish_targets(bucket_name, bucket_prefix) artifacts = _collect_artifacts() if not artifacts: @@ -287,9 +358,10 @@ def main(version: Optional[str] = None, app_dir: Optional[str] = None) -> None: signature = _read_signature(primary) client = _create_minio_client() - _ensure_bucket(client, bucket_name) - existing_latest_json = _read_existing_latest_json(client, bucket_name) + local_dist_dir = PROJECT_DIR / "dist" + local_dist_dir.mkdir(parents=True, exist_ok=True) + latest_json_path = local_dist_dir / "latest.json" latest_payload = _build_latest_payload( version=resolved_version, platform=platform, @@ -297,34 +369,21 @@ def main(version: Optional[str] = None, app_dir: Optional[str] = None) -> None: signature=signature, artifact_base_url=artifact_base_url, app_dir=bucket_prefix, - existing_payload=existing_latest_json, ) latest_json = json.dumps(latest_payload, separators=(",", ":")) - - local_dist_dir = PROJECT_DIR / "dist" - local_dist_dir.mkdir(parents=True, exist_ok=True) - latest_json_path = local_dist_dir / "latest.json" latest_json_path.write_text(latest_json + "\n", encoding="utf-8") - for artifact in artifacts: - object_segments = [part for part in (resolved_version, artifact.name) if part] - object_name = "/".join(object_segments) - client.fput_object(bucket_name, object_name, str(artifact)) - print(f"Uploaded {artifact.name} -> s3://{bucket_name}/{object_name}") - - latest_object_name = "latest.json" - latest_bytes = latest_json.encode("utf-8") - client.put_object( - bucket_name=bucket_name, - object_name=latest_object_name, - data=io.BytesIO(latest_bytes), - length=len(latest_bytes), - content_type="application/json", - ) - print( - f"Uploaded latest.json for {platform} -> " - f"s3://{bucket_name}/{latest_object_name}" - ) + for target in publish_targets: + _publish_target( + client=client, + target=target, + version=resolved_version, + platform=platform, + artifacts=artifacts, + primary_artifact=primary, + signature=signature, + artifact_base_url=artifact_base_url, + ) if __name__ == "__main__": diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index d347ef6..c004829 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -64,6 +64,7 @@ "updater": { "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDdDNEJGRTZERTREOTc5QjkKUldTNWVkbmtiZjVMZk02YzdpenVLOVltREJpRkFrc2F4dUwzaTV5M3pacFRVR0tmd25xSGtjK1MK", "endpoints": [ + "https://cdn.cbsk-tech.de/gitty/latest.json", "https://cdn.cbsk-tech.de/gitlite/latest.json" ] }