synapse-sdk 1.0.0a11__py3-none-any.whl → 2026.1.1b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +9 -8
- synapse_sdk/cli/agent/__init__.py +25 -0
- synapse_sdk/cli/agent/config.py +104 -0
- synapse_sdk/cli/agent/select.py +197 -0
- synapse_sdk/cli/auth.py +104 -0
- synapse_sdk/cli/main.py +1025 -0
- synapse_sdk/cli/plugin/__init__.py +58 -0
- synapse_sdk/cli/plugin/create.py +566 -0
- synapse_sdk/cli/plugin/job.py +196 -0
- synapse_sdk/cli/plugin/publish.py +322 -0
- synapse_sdk/cli/plugin/run.py +131 -0
- synapse_sdk/cli/plugin/test.py +200 -0
- synapse_sdk/clients/README.md +239 -0
- synapse_sdk/clients/__init__.py +5 -0
- synapse_sdk/clients/_template.py +266 -0
- synapse_sdk/clients/agent/__init__.py +84 -29
- synapse_sdk/clients/agent/async_ray.py +289 -0
- synapse_sdk/clients/agent/container.py +83 -0
- synapse_sdk/clients/agent/plugin.py +101 -0
- synapse_sdk/clients/agent/ray.py +296 -39
- synapse_sdk/clients/backend/__init__.py +152 -12
- synapse_sdk/clients/backend/annotation.py +164 -22
- synapse_sdk/clients/backend/core.py +101 -0
- synapse_sdk/clients/backend/data_collection.py +292 -0
- synapse_sdk/clients/backend/hitl.py +87 -0
- synapse_sdk/clients/backend/integration.py +374 -46
- synapse_sdk/clients/backend/ml.py +134 -22
- synapse_sdk/clients/backend/models.py +247 -0
- synapse_sdk/clients/base.py +538 -59
- synapse_sdk/clients/exceptions.py +35 -7
- synapse_sdk/clients/pipeline/__init__.py +5 -0
- synapse_sdk/clients/pipeline/client.py +636 -0
- synapse_sdk/clients/protocols.py +178 -0
- synapse_sdk/clients/utils.py +86 -8
- synapse_sdk/clients/validation.py +58 -0
- synapse_sdk/enums.py +76 -0
- synapse_sdk/exceptions.py +168 -0
- synapse_sdk/integrations/__init__.py +74 -0
- synapse_sdk/integrations/_base.py +119 -0
- synapse_sdk/integrations/_context.py +53 -0
- synapse_sdk/integrations/ultralytics/__init__.py +78 -0
- synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
- synapse_sdk/integrations/ultralytics/_patches.py +124 -0
- synapse_sdk/loggers.py +476 -95
- synapse_sdk/mcp/MCP.md +69 -0
- synapse_sdk/mcp/__init__.py +48 -0
- synapse_sdk/mcp/__main__.py +6 -0
- synapse_sdk/mcp/config.py +349 -0
- synapse_sdk/mcp/prompts/__init__.py +4 -0
- synapse_sdk/mcp/resources/__init__.py +4 -0
- synapse_sdk/mcp/server.py +1352 -0
- synapse_sdk/mcp/tools/__init__.py +6 -0
- synapse_sdk/plugins/__init__.py +133 -9
- synapse_sdk/plugins/action.py +229 -0
- synapse_sdk/plugins/actions/__init__.py +82 -0
- synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
- synapse_sdk/plugins/actions/dataset/action.py +471 -0
- synapse_sdk/plugins/actions/export/__init__.py +55 -0
- synapse_sdk/plugins/actions/export/action.py +183 -0
- synapse_sdk/plugins/actions/export/context.py +59 -0
- synapse_sdk/plugins/actions/inference/__init__.py +84 -0
- synapse_sdk/plugins/actions/inference/action.py +285 -0
- synapse_sdk/plugins/actions/inference/context.py +81 -0
- synapse_sdk/plugins/actions/inference/deployment.py +322 -0
- synapse_sdk/plugins/actions/inference/serve.py +252 -0
- synapse_sdk/plugins/actions/train/__init__.py +54 -0
- synapse_sdk/plugins/actions/train/action.py +326 -0
- synapse_sdk/plugins/actions/train/context.py +57 -0
- synapse_sdk/plugins/actions/upload/__init__.py +49 -0
- synapse_sdk/plugins/actions/upload/action.py +165 -0
- synapse_sdk/plugins/actions/upload/context.py +61 -0
- synapse_sdk/plugins/config.py +98 -0
- synapse_sdk/plugins/context/__init__.py +109 -0
- synapse_sdk/plugins/context/env.py +113 -0
- synapse_sdk/plugins/datasets/__init__.py +113 -0
- synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
- synapse_sdk/plugins/datasets/converters/base.py +347 -0
- synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
- synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
- synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
- synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
- synapse_sdk/plugins/datasets/formats/dm.py +351 -0
- synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
- synapse_sdk/plugins/decorators.py +83 -0
- synapse_sdk/plugins/discovery.py +790 -0
- synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
- synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
- synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
- synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
- synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
- synapse_sdk/plugins/docs/README.md +513 -0
- synapse_sdk/plugins/docs/STEP.md +656 -0
- synapse_sdk/plugins/enums.py +70 -10
- synapse_sdk/plugins/errors.py +92 -0
- synapse_sdk/plugins/executors/__init__.py +43 -0
- synapse_sdk/plugins/executors/local.py +99 -0
- synapse_sdk/plugins/executors/ray/__init__.py +18 -0
- synapse_sdk/plugins/executors/ray/base.py +282 -0
- synapse_sdk/plugins/executors/ray/job.py +298 -0
- synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
- synapse_sdk/plugins/executors/ray/packaging.py +137 -0
- synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
- synapse_sdk/plugins/executors/ray/task.py +257 -0
- synapse_sdk/plugins/models/__init__.py +26 -0
- synapse_sdk/plugins/models/logger.py +173 -0
- synapse_sdk/plugins/models/pipeline.py +25 -0
- synapse_sdk/plugins/pipelines/__init__.py +81 -0
- synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
- synapse_sdk/plugins/pipelines/context.py +107 -0
- synapse_sdk/plugins/pipelines/display.py +311 -0
- synapse_sdk/plugins/runner.py +114 -0
- synapse_sdk/plugins/schemas/__init__.py +19 -0
- synapse_sdk/plugins/schemas/results.py +152 -0
- synapse_sdk/plugins/steps/__init__.py +63 -0
- synapse_sdk/plugins/steps/base.py +128 -0
- synapse_sdk/plugins/steps/context.py +90 -0
- synapse_sdk/plugins/steps/orchestrator.py +128 -0
- synapse_sdk/plugins/steps/registry.py +103 -0
- synapse_sdk/plugins/steps/utils/__init__.py +20 -0
- synapse_sdk/plugins/steps/utils/logging.py +85 -0
- synapse_sdk/plugins/steps/utils/timing.py +71 -0
- synapse_sdk/plugins/steps/utils/validation.py +68 -0
- synapse_sdk/plugins/templates/__init__.py +50 -0
- synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
- synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
- synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
- synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
- synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
- synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
- synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
- synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
- synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
- synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
- synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
- synapse_sdk/plugins/testing/__init__.py +25 -0
- synapse_sdk/plugins/testing/sample_actions.py +98 -0
- synapse_sdk/plugins/types.py +206 -0
- synapse_sdk/plugins/upload.py +595 -64
- synapse_sdk/plugins/utils.py +325 -37
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/__init__.py +1 -0
- synapse_sdk/utils/auth.py +74 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +449 -0
- synapse_sdk/utils/file/checksum.py +167 -0
- synapse_sdk/utils/file/download.py +286 -0
- synapse_sdk/utils/file/io.py +129 -0
- synapse_sdk/utils/file/requirements.py +36 -0
- synapse_sdk/utils/network.py +168 -0
- synapse_sdk/utils/storage/__init__.py +238 -0
- synapse_sdk/utils/storage/config.py +188 -0
- synapse_sdk/utils/storage/errors.py +52 -0
- synapse_sdk/utils/storage/providers/__init__.py +13 -0
- synapse_sdk/utils/storage/providers/base.py +76 -0
- synapse_sdk/utils/storage/providers/gcs.py +168 -0
- synapse_sdk/utils/storage/providers/http.py +250 -0
- synapse_sdk/utils/storage/providers/local.py +126 -0
- synapse_sdk/utils/storage/providers/s3.py +177 -0
- synapse_sdk/utils/storage/providers/sftp.py +208 -0
- synapse_sdk/utils/storage/registry.py +125 -0
- synapse_sdk/utils/websocket.py +99 -0
- synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
- synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
- synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
- locale/en/LC_MESSAGES/messages.mo +0 -0
- locale/en/LC_MESSAGES/messages.po +0 -39
- locale/ko/LC_MESSAGES/messages.mo +0 -0
- locale/ko/LC_MESSAGES/messages.po +0 -34
- synapse_sdk/cli/create_plugin.py +0 -10
- synapse_sdk/clients/agent/core.py +0 -7
- synapse_sdk/clients/agent/service.py +0 -15
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/clients/ray/__init__.py +0 -6
- synapse_sdk/clients/ray/core.py +0 -22
- synapse_sdk/clients/ray/serve.py +0 -20
- synapse_sdk/i18n.py +0 -35
- synapse_sdk/plugins/categories/__init__.py +0 -0
- synapse_sdk/plugins/categories/base.py +0 -235
- synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
- synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
- synapse_sdk/plugins/categories/decorators.py +0 -13
- synapse_sdk/plugins/categories/export/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/export.py +0 -10
- synapse_sdk/plugins/categories/import/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
- synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
- synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
- synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
- synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
- synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
- synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
- synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
- synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
- synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
- synapse_sdk/plugins/categories/registry.py +0 -16
- synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
- synapse_sdk/plugins/categories/templates.py +0 -32
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/cli/publish.py +0 -37
- synapse_sdk/plugins/cli/run.py +0 -67
- synapse_sdk/plugins/exceptions.py +0 -22
- synapse_sdk/plugins/models.py +0 -121
- synapse_sdk/plugins/templates/cookiecutter.json +0 -11
- synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
- synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
- synapse_sdk/shared/enums.py +0 -8
- synapse_sdk/utils/debug.py +0 -5
- synapse_sdk/utils/file.py +0 -87
- synapse_sdk/utils/module_loading.py +0 -29
- synapse_sdk/utils/pydantic/__init__.py +0 -0
- synapse_sdk/utils/pydantic/config.py +0 -4
- synapse_sdk/utils/pydantic/errors.py +0 -33
- synapse_sdk/utils/pydantic/validators.py +0 -7
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk/utils/string.py +0 -11
- synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
- synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
- synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
synapse_sdk/plugins/upload.py
CHANGED
|
@@ -1,89 +1,620 @@
|
|
|
1
|
+
"""Plugin upload utilities for archiving and uploading plugins to storage."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
1
5
|
import subprocess
|
|
2
6
|
import tempfile
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import StrEnum
|
|
3
10
|
from pathlib import Path
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
from synapse_sdk.plugins.errors import ArchiveError, BuildError, PluginUploadError
|
|
14
|
+
from synapse_sdk.utils.file.archive import create_archive_from_git, get_archive_size
|
|
15
|
+
from synapse_sdk.utils.file.checksum import calculate_checksum
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from synapse_sdk.utils.storage import StorageProtocol
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Progress callback signature: (stage, current, total)
|
|
22
|
+
UploadProgressCallback = Callable[[str, int, int], None]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PackageManager(StrEnum):
|
|
26
|
+
"""Supported package managers for building wheels."""
|
|
27
|
+
|
|
28
|
+
UV = 'uv'
|
|
29
|
+
POETRY = 'poetry'
|
|
30
|
+
PIP = 'pip'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class UploadStage(StrEnum):
|
|
34
|
+
"""Upload operation stages for progress tracking."""
|
|
35
|
+
|
|
36
|
+
ARCHIVING = 'archiving'
|
|
37
|
+
CHECKSUMMING = 'checksumming'
|
|
38
|
+
BUILDING = 'building'
|
|
39
|
+
UPLOADING = 'uploading'
|
|
40
|
+
VERIFYING = 'verifying'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class UploadResult:
|
|
45
|
+
"""Result of a plugin upload operation.
|
|
46
|
+
|
|
47
|
+
Attributes:
|
|
48
|
+
url: Storage URL of uploaded file.
|
|
49
|
+
checksum: MD5 checksum of uploaded file.
|
|
50
|
+
filename: Name of uploaded file.
|
|
51
|
+
size: Size in bytes.
|
|
52
|
+
is_cached: True if file already existed in storage.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
url: str
|
|
56
|
+
checksum: str
|
|
57
|
+
filename: str
|
|
58
|
+
size: int
|
|
59
|
+
is_cached: bool = False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class BuildConfig:
|
|
64
|
+
"""Configuration for wheel building.
|
|
65
|
+
|
|
66
|
+
Attributes:
|
|
67
|
+
package_manager: Build tool to use (uv, poetry, pip).
|
|
68
|
+
python_path: Path to Python interpreter (auto-detected if None).
|
|
69
|
+
extra_args: Additional arguments to pass to build command.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
package_manager: PackageManager = PackageManager.UV
|
|
73
|
+
python_path: Path | None = None
|
|
74
|
+
extra_args: list[str] = field(default_factory=list)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _get_storage(storage: StorageProtocol | dict[str, Any]) -> StorageProtocol:
|
|
78
|
+
"""Convert storage config to StorageProtocol instance.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
storage: StorageProtocol instance or config dict.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
StorageProtocol instance.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
PluginUploadError: If storage configuration is invalid.
|
|
88
|
+
"""
|
|
89
|
+
# Check if it's already a StorageProtocol instance
|
|
90
|
+
# We check for the presence of required methods
|
|
91
|
+
if hasattr(storage, 'upload') and hasattr(storage, 'exists') and hasattr(storage, 'get_url'):
|
|
92
|
+
return storage # type: ignore[return-value]
|
|
93
|
+
|
|
94
|
+
# Convert dict config to storage instance
|
|
95
|
+
if isinstance(storage, dict):
|
|
96
|
+
from synapse_sdk.utils.storage import get_storage
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
return get_storage(storage)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
raise PluginUploadError(
|
|
102
|
+
f'Invalid storage configuration: {e}',
|
|
103
|
+
details={'config': storage},
|
|
104
|
+
) from e
|
|
105
|
+
|
|
106
|
+
raise PluginUploadError(
|
|
107
|
+
'Invalid storage type. Expected StorageProtocol or dict.',
|
|
108
|
+
details={'type': type(storage).__name__},
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _report_progress(
|
|
113
|
+
callback: UploadProgressCallback | None,
|
|
114
|
+
stage: UploadStage,
|
|
115
|
+
current: int,
|
|
116
|
+
total: int,
|
|
117
|
+
) -> None:
|
|
118
|
+
"""Report progress if callback is provided."""
|
|
119
|
+
if callback:
|
|
120
|
+
callback(stage.value, current, total)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _get_build_command(config: BuildConfig, source_path: Path) -> list[str]:
|
|
124
|
+
"""Generate build command based on package manager.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
config: Build configuration.
|
|
128
|
+
source_path: Plugin source directory.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Command as list of strings.
|
|
132
|
+
|
|
133
|
+
Raises:
|
|
134
|
+
BuildError: If package manager is not supported.
|
|
135
|
+
"""
|
|
136
|
+
python = str(config.python_path) if config.python_path else 'python'
|
|
137
|
+
|
|
138
|
+
match config.package_manager:
|
|
139
|
+
case PackageManager.UV:
|
|
140
|
+
return ['uv', 'build', '--wheel', *config.extra_args]
|
|
141
|
+
case PackageManager.POETRY:
|
|
142
|
+
return ['poetry', 'build', '--format', 'wheel', *config.extra_args]
|
|
143
|
+
case PackageManager.PIP:
|
|
144
|
+
return [python, '-m', 'build', '--wheel', *config.extra_args]
|
|
145
|
+
case _:
|
|
146
|
+
raise BuildError(
|
|
147
|
+
f'Unsupported package manager: {config.package_manager}',
|
|
148
|
+
details={'package_manager': config.package_manager},
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def archive_plugin(
|
|
153
|
+
source_path: str | Path,
|
|
154
|
+
archive_path: str | Path | None = None,
|
|
155
|
+
*,
|
|
156
|
+
use_git: bool = True,
|
|
157
|
+
progress_callback: UploadProgressCallback | None = None,
|
|
158
|
+
) -> tuple[Path, str]:
|
|
159
|
+
"""Archive a plugin directory.
|
|
160
|
+
|
|
161
|
+
Creates a ZIP archive of the plugin source code. When use_git=True,
|
|
162
|
+
uses git ls-files to determine which files to include.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
source_path: Plugin source directory.
|
|
166
|
+
archive_path: Output path (auto-generated in temp dir if None).
|
|
167
|
+
use_git: Use git ls-files for file selection.
|
|
168
|
+
progress_callback: Optional progress callback.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Tuple of (archive_path, checksum).
|
|
172
|
+
|
|
173
|
+
Raises:
|
|
174
|
+
ArchiveError: If archiving fails.
|
|
175
|
+
FileNotFoundError: If source_path does not exist.
|
|
176
|
+
|
|
177
|
+
Example:
|
|
178
|
+
>>> archive_path, checksum = archive_plugin('/path/to/plugin')
|
|
179
|
+
>>> print(f'Created {archive_path} with checksum {checksum}')
|
|
180
|
+
"""
|
|
181
|
+
source = Path(source_path).resolve()
|
|
182
|
+
|
|
183
|
+
if not source.exists():
|
|
184
|
+
raise FileNotFoundError(f'Source path not found: {source}')
|
|
185
|
+
|
|
186
|
+
# Generate archive path if not provided
|
|
187
|
+
if archive_path is None:
|
|
188
|
+
temp_dir = tempfile.mkdtemp()
|
|
189
|
+
archive = Path(temp_dir) / 'archive.zip'
|
|
190
|
+
else:
|
|
191
|
+
archive = Path(archive_path).resolve()
|
|
192
|
+
|
|
193
|
+
_report_progress(progress_callback, UploadStage.ARCHIVING, 0, 100)
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
if use_git:
|
|
197
|
+
create_archive_from_git(source, archive)
|
|
198
|
+
else:
|
|
199
|
+
from synapse_sdk.utils.file.archive import create_archive
|
|
200
|
+
|
|
201
|
+
create_archive(source, archive)
|
|
202
|
+
except Exception as e:
|
|
203
|
+
raise ArchiveError(
|
|
204
|
+
f'Failed to create archive: {e}',
|
|
205
|
+
details={'source': str(source), 'archive': str(archive)},
|
|
206
|
+
) from e
|
|
207
|
+
|
|
208
|
+
_report_progress(progress_callback, UploadStage.ARCHIVING, 100, 100)
|
|
209
|
+
|
|
210
|
+
# Calculate checksum
|
|
211
|
+
_report_progress(progress_callback, UploadStage.CHECKSUMMING, 0, 1)
|
|
212
|
+
checksum = calculate_checksum(archive)
|
|
213
|
+
_report_progress(progress_callback, UploadStage.CHECKSUMMING, 1, 1)
|
|
4
214
|
|
|
5
|
-
|
|
6
|
-
from synapse_sdk.utils.file import calculate_checksum, download_file
|
|
7
|
-
from synapse_sdk.utils.storage import get_storage
|
|
215
|
+
return archive, checksum
|
|
8
216
|
|
|
9
217
|
|
|
10
|
-
def
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
218
|
+
def archive_and_upload(
|
|
219
|
+
source_path: str | Path,
|
|
220
|
+
storage: StorageProtocol | dict[str, Any],
|
|
221
|
+
*,
|
|
222
|
+
target_prefix: str = '',
|
|
223
|
+
use_git: bool = True,
|
|
224
|
+
skip_existing: bool = True,
|
|
225
|
+
progress_callback: UploadProgressCallback | None = None,
|
|
226
|
+
) -> UploadResult:
|
|
227
|
+
"""Archive plugin and upload to storage.
|
|
14
228
|
|
|
229
|
+
Creates a ZIP archive with checksum-based naming (dev-{checksum}.zip).
|
|
230
|
+
If skip_existing=True and file exists in storage, returns cached URL.
|
|
15
231
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
232
|
+
Args:
|
|
233
|
+
source_path: Plugin source directory.
|
|
234
|
+
storage: Storage provider or config dict.
|
|
235
|
+
target_prefix: Optional prefix for target path.
|
|
236
|
+
use_git: Use git ls-files for file selection.
|
|
237
|
+
skip_existing: Skip upload if file exists in storage.
|
|
238
|
+
progress_callback: Optional progress callback.
|
|
23
239
|
|
|
240
|
+
Returns:
|
|
241
|
+
UploadResult with URL, checksum, and metadata.
|
|
24
242
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
archive_path = dist_path / 'archive.zip'
|
|
243
|
+
Raises:
|
|
244
|
+
ArchiveError: If archiving fails.
|
|
245
|
+
PluginUploadError: If upload fails.
|
|
29
246
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
247
|
+
Example:
|
|
248
|
+
>>> result = archive_and_upload(
|
|
249
|
+
... '/path/to/plugin',
|
|
250
|
+
... {'provider': 's3', 'configuration': {...}},
|
|
251
|
+
... )
|
|
252
|
+
>>> print(result.url)
|
|
253
|
+
"""
|
|
254
|
+
storage_provider = _get_storage(storage)
|
|
255
|
+
source = Path(source_path).resolve()
|
|
33
256
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
257
|
+
# Create archive and get checksum
|
|
258
|
+
archive_path, checksum = archive_plugin(
|
|
259
|
+
source,
|
|
260
|
+
use_git=use_git,
|
|
261
|
+
progress_callback=progress_callback,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Build target filename with checksum
|
|
265
|
+
filename = f'dev-{checksum}.zip'
|
|
266
|
+
target_path = f'{target_prefix}{filename}' if target_prefix else filename
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
# Check if already exists in storage
|
|
270
|
+
if skip_existing and storage_provider.exists(target_path):
|
|
271
|
+
url = storage_provider.get_url(target_path)
|
|
272
|
+
return UploadResult(
|
|
273
|
+
url=url,
|
|
274
|
+
checksum=checksum,
|
|
275
|
+
filename=filename,
|
|
276
|
+
size=get_archive_size(archive_path),
|
|
277
|
+
is_cached=True,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# Upload to storage
|
|
281
|
+
_report_progress(progress_callback, UploadStage.UPLOADING, 0, 100)
|
|
282
|
+
url = storage_provider.upload(archive_path, target_path)
|
|
283
|
+
_report_progress(progress_callback, UploadStage.UPLOADING, 100, 100)
|
|
284
|
+
|
|
285
|
+
return UploadResult(
|
|
286
|
+
url=url,
|
|
287
|
+
checksum=checksum,
|
|
288
|
+
filename=filename,
|
|
289
|
+
size=get_archive_size(archive_path),
|
|
290
|
+
is_cached=False,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
except Exception as e:
|
|
294
|
+
if isinstance(e, PluginUploadError):
|
|
295
|
+
raise
|
|
296
|
+
raise PluginUploadError(
|
|
297
|
+
f'Failed to upload archive: {e}',
|
|
298
|
+
details={'target': target_path},
|
|
299
|
+
) from e
|
|
300
|
+
finally:
|
|
301
|
+
# Clean up temp archive
|
|
302
|
+
if archive_path.exists():
|
|
303
|
+
archive_path.unlink(missing_ok=True)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def modify_wheel_build_tag(
|
|
307
|
+
wheel_path: str | Path,
|
|
308
|
+
build_tag: str,
|
|
309
|
+
) -> Path:
|
|
310
|
+
"""Modify wheel filename to embed build tag (checksum).
|
|
311
|
+
|
|
312
|
+
Converts: package-1.0.0-py3-none-any.whl
|
|
313
|
+
To: package-1.0.0+{build_tag}-py3-none-any.whl
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
wheel_path: Path to wheel file.
|
|
317
|
+
build_tag: Build tag to embed (typically checksum).
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Path to renamed wheel file.
|
|
321
|
+
|
|
322
|
+
Raises:
|
|
323
|
+
ValueError: If wheel filename format is invalid.
|
|
324
|
+
|
|
325
|
+
Example:
|
|
326
|
+
>>> new_path = modify_wheel_build_tag('/path/to/pkg-1.0.0-py3-none-any.whl', 'abc123')
|
|
327
|
+
>>> print(new_path.name)
|
|
328
|
+
'pkg-1.0.0+abc123-py3-none-any.whl'
|
|
329
|
+
"""
|
|
330
|
+
path = Path(wheel_path)
|
|
37
331
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
file_path.unlink()
|
|
42
|
-
return storage.upload(str(checksum_archive_path), checksum_archive_path.name)
|
|
332
|
+
# Wheel filename format: {name}-{version}[-{build}]-{python}-{abi}-{platform}.whl
|
|
333
|
+
# Minimum components: name-version-python-abi-platform.whl = 5 parts
|
|
334
|
+
parts = path.stem.split('-')
|
|
43
335
|
|
|
336
|
+
if len(parts) < 5:
|
|
337
|
+
raise ValueError(f'Invalid wheel filename format: {path.name}')
|
|
44
338
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
archive_path = dist_path / 'archive.zip'
|
|
339
|
+
# Version is always the second part
|
|
340
|
+
# It may already contain a build tag (after +)
|
|
341
|
+
version = parts[1].split('+')[0]
|
|
49
342
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
checksum_archive_path = dist_path / f'dev-{checksum}.zip'
|
|
343
|
+
# Insert build tag into version
|
|
344
|
+
parts[1] = f'{version}+{build_tag}'
|
|
53
345
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return storage.get_url(wheel_path.name)
|
|
346
|
+
# Reconstruct filename
|
|
347
|
+
new_name = '-'.join(parts) + '.whl'
|
|
348
|
+
new_path = path.parent / new_name
|
|
58
349
|
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
file_path.unlink()
|
|
350
|
+
# Rename the file
|
|
351
|
+
path.rename(new_path)
|
|
62
352
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
353
|
+
return new_path
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def build_and_upload(
|
|
357
|
+
source_path: str | Path,
|
|
358
|
+
storage: StorageProtocol | dict[str, Any],
|
|
359
|
+
*,
|
|
360
|
+
build_config: BuildConfig | None = None,
|
|
361
|
+
target_prefix: str = '',
|
|
362
|
+
skip_existing: bool = True,
|
|
363
|
+
progress_callback: UploadProgressCallback | None = None,
|
|
364
|
+
) -> UploadResult:
|
|
365
|
+
"""Build wheel and upload to storage.
|
|
366
|
+
|
|
367
|
+
Creates archive, calculates checksum, builds wheel, embeds checksum
|
|
368
|
+
in wheel filename build tag, and uploads to storage.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
source_path: Plugin source directory with pyproject.toml.
|
|
372
|
+
storage: Storage provider or config dict.
|
|
373
|
+
build_config: Build configuration (defaults to uv).
|
|
374
|
+
target_prefix: Optional prefix for target path.
|
|
375
|
+
skip_existing: Skip upload if file exists in storage.
|
|
376
|
+
progress_callback: Optional progress callback.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
UploadResult with wheel URL, checksum, and metadata.
|
|
380
|
+
|
|
381
|
+
Raises:
|
|
382
|
+
BuildError: If wheel build fails.
|
|
383
|
+
PluginUploadError: If upload fails.
|
|
384
|
+
|
|
385
|
+
Example:
|
|
386
|
+
>>> result = build_and_upload(
|
|
387
|
+
... '/path/to/plugin',
|
|
388
|
+
... {'provider': 's3', 'configuration': {...}},
|
|
389
|
+
... build_config=BuildConfig(package_manager=PackageManager.UV),
|
|
390
|
+
... )
|
|
391
|
+
>>> print(result.url)
|
|
392
|
+
"""
|
|
393
|
+
storage_provider = _get_storage(storage)
|
|
394
|
+
source = Path(source_path).resolve()
|
|
395
|
+
|
|
396
|
+
if build_config is None:
|
|
397
|
+
build_config = BuildConfig()
|
|
398
|
+
|
|
399
|
+
# Check for pyproject.toml
|
|
400
|
+
if not (source / 'pyproject.toml').exists():
|
|
401
|
+
raise BuildError(
|
|
402
|
+
'No pyproject.toml found in source directory',
|
|
403
|
+
details={'source': str(source)},
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
# Create archive and get checksum
|
|
407
|
+
archive_path, checksum = archive_plugin(
|
|
408
|
+
source,
|
|
409
|
+
use_git=True,
|
|
410
|
+
progress_callback=progress_callback,
|
|
70
411
|
)
|
|
71
|
-
wheel_path = next(dist_path.glob('*.whl'), None)
|
|
72
412
|
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
|
|
413
|
+
# Check if already exists in storage (use checksum-based wheel name pattern)
|
|
414
|
+
# The wheel name will contain the checksum as build tag
|
|
415
|
+
if skip_existing:
|
|
416
|
+
# Try to find existing wheel with this checksum
|
|
417
|
+
# Format: name-version+checksum-py3-none-any.whl
|
|
418
|
+
# We can't know the exact name without building, so we check after build
|
|
419
|
+
pass
|
|
420
|
+
|
|
421
|
+
# Build wheel
|
|
422
|
+
_report_progress(progress_callback, UploadStage.BUILDING, 0, 100)
|
|
423
|
+
|
|
424
|
+
dist_dir = source / 'dist'
|
|
425
|
+
dist_dir.mkdir(exist_ok=True)
|
|
426
|
+
|
|
427
|
+
# Clean existing wheel files
|
|
428
|
+
for whl_file in dist_dir.glob('*.whl'):
|
|
429
|
+
whl_file.unlink()
|
|
430
|
+
|
|
431
|
+
build_cmd = _get_build_command(build_config, source)
|
|
432
|
+
|
|
433
|
+
try:
|
|
434
|
+
subprocess.run(
|
|
435
|
+
build_cmd,
|
|
436
|
+
cwd=source,
|
|
437
|
+
check=True,
|
|
438
|
+
capture_output=True,
|
|
439
|
+
text=True,
|
|
440
|
+
)
|
|
441
|
+
except subprocess.CalledProcessError as e:
|
|
442
|
+
raise BuildError(
|
|
443
|
+
f'Wheel build failed: {e.stderr}',
|
|
444
|
+
details={
|
|
445
|
+
'command': ' '.join(build_cmd),
|
|
446
|
+
'returncode': e.returncode,
|
|
447
|
+
'stdout': e.stdout,
|
|
448
|
+
'stderr': e.stderr,
|
|
449
|
+
},
|
|
450
|
+
) from e
|
|
451
|
+
except FileNotFoundError as e:
|
|
452
|
+
raise BuildError(
|
|
453
|
+
f'Build command not found: {build_cmd[0]}. Is it installed?',
|
|
454
|
+
details={'command': build_cmd[0]},
|
|
455
|
+
) from e
|
|
456
|
+
|
|
457
|
+
_report_progress(progress_callback, UploadStage.BUILDING, 100, 100)
|
|
458
|
+
|
|
459
|
+
# Find built wheel
|
|
460
|
+
wheel_files = list(dist_dir.glob('*.whl'))
|
|
461
|
+
if not wheel_files:
|
|
462
|
+
raise BuildError(
|
|
463
|
+
'No wheel file found after build',
|
|
464
|
+
details={'dist_dir': str(dist_dir)},
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
wheel_path = wheel_files[0]
|
|
468
|
+
|
|
469
|
+
# Embed checksum in wheel filename
|
|
470
|
+
wheel_path = modify_wheel_build_tag(wheel_path, checksum)
|
|
471
|
+
|
|
472
|
+
filename = wheel_path.name
|
|
473
|
+
target_path = f'{target_prefix}{filename}' if target_prefix else filename
|
|
474
|
+
|
|
475
|
+
try:
|
|
476
|
+
# Check if already exists
|
|
477
|
+
if skip_existing and storage_provider.exists(target_path):
|
|
478
|
+
url = storage_provider.get_url(target_path)
|
|
479
|
+
return UploadResult(
|
|
480
|
+
url=url,
|
|
481
|
+
checksum=checksum,
|
|
482
|
+
filename=filename,
|
|
483
|
+
size=wheel_path.stat().st_size,
|
|
484
|
+
is_cached=True,
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
# Upload wheel
|
|
488
|
+
_report_progress(progress_callback, UploadStage.UPLOADING, 0, 100)
|
|
489
|
+
url = storage_provider.upload(wheel_path, target_path)
|
|
490
|
+
_report_progress(progress_callback, UploadStage.UPLOADING, 100, 100)
|
|
491
|
+
|
|
492
|
+
return UploadResult(
|
|
493
|
+
url=url,
|
|
494
|
+
checksum=checksum,
|
|
495
|
+
filename=filename,
|
|
496
|
+
size=wheel_path.stat().st_size,
|
|
497
|
+
is_cached=False,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
except Exception as e:
|
|
501
|
+
if isinstance(e, (PluginUploadError, BuildError)):
|
|
502
|
+
raise
|
|
503
|
+
raise PluginUploadError(
|
|
504
|
+
f'Failed to upload wheel: {e}',
|
|
505
|
+
details={'target': target_path},
|
|
506
|
+
) from e
|
|
507
|
+
finally:
|
|
508
|
+
# Clean up temp archive
|
|
509
|
+
if archive_path.exists():
|
|
510
|
+
archive_path.unlink(missing_ok=True)
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def download_and_upload(
|
|
514
|
+
source_url: str,
|
|
515
|
+
storage: StorageProtocol | dict[str, Any],
|
|
516
|
+
*,
|
|
517
|
+
target_prefix: str = '',
|
|
518
|
+
skip_existing: bool = True,
|
|
519
|
+
progress_callback: UploadProgressCallback | None = None,
|
|
520
|
+
) -> UploadResult:
|
|
521
|
+
"""Download file from URL and upload to storage.
|
|
522
|
+
|
|
523
|
+
Downloads the file, calculates checksum, and re-uploads with
|
|
524
|
+
checksum-based naming to the target storage.
|
|
525
|
+
|
|
526
|
+
Args:
|
|
527
|
+
source_url: URL to download from.
|
|
528
|
+
storage: Storage provider or config dict.
|
|
529
|
+
target_prefix: Optional prefix for target path.
|
|
530
|
+
skip_existing: Skip upload if file exists in storage.
|
|
531
|
+
progress_callback: Optional progress callback.
|
|
532
|
+
|
|
533
|
+
Returns:
|
|
534
|
+
UploadResult with storage URL, checksum, and metadata.
|
|
535
|
+
|
|
536
|
+
Raises:
|
|
537
|
+
PluginUploadError: If download or upload fails.
|
|
538
|
+
|
|
539
|
+
Example:
|
|
540
|
+
>>> result = download_and_upload(
|
|
541
|
+
... 'https://example.com/plugin.zip',
|
|
542
|
+
... {'provider': 's3', 'configuration': {...}},
|
|
543
|
+
... )
|
|
544
|
+
>>> print(result.url)
|
|
545
|
+
"""
|
|
546
|
+
from synapse_sdk.utils.file.download import download_file
|
|
547
|
+
|
|
548
|
+
storage_provider = _get_storage(storage)
|
|
549
|
+
|
|
550
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
551
|
+
temp_path = Path(temp_dir)
|
|
552
|
+
|
|
553
|
+
# Download file
|
|
554
|
+
try:
|
|
555
|
+
downloaded_path = download_file(source_url, temp_path)
|
|
556
|
+
except Exception as e:
|
|
557
|
+
raise PluginUploadError(
|
|
558
|
+
f'Failed to download file: {e}',
|
|
559
|
+
details={'url': source_url},
|
|
560
|
+
) from e
|
|
561
|
+
|
|
562
|
+
# Calculate checksum
|
|
563
|
+
_report_progress(progress_callback, UploadStage.CHECKSUMMING, 0, 1)
|
|
564
|
+
checksum = calculate_checksum(downloaded_path)
|
|
565
|
+
_report_progress(progress_callback, UploadStage.CHECKSUMMING, 1, 1)
|
|
566
|
+
|
|
567
|
+
# Build target filename with checksum
|
|
568
|
+
filename = f'dev-{checksum}.zip'
|
|
569
|
+
target_path = f'{target_prefix}{filename}' if target_prefix else filename
|
|
570
|
+
|
|
571
|
+
try:
|
|
572
|
+
# Check if already exists
|
|
573
|
+
if skip_existing and storage_provider.exists(target_path):
|
|
574
|
+
url = storage_provider.get_url(target_path)
|
|
575
|
+
return UploadResult(
|
|
576
|
+
url=url,
|
|
577
|
+
checksum=checksum,
|
|
578
|
+
filename=filename,
|
|
579
|
+
size=downloaded_path.stat().st_size,
|
|
580
|
+
is_cached=True,
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
# Upload to storage
|
|
584
|
+
_report_progress(progress_callback, UploadStage.UPLOADING, 0, 100)
|
|
585
|
+
url = storage_provider.upload(downloaded_path, target_path)
|
|
586
|
+
_report_progress(progress_callback, UploadStage.UPLOADING, 100, 100)
|
|
76
587
|
|
|
77
|
-
|
|
588
|
+
return UploadResult(
|
|
589
|
+
url=url,
|
|
590
|
+
checksum=checksum,
|
|
591
|
+
filename=filename,
|
|
592
|
+
size=downloaded_path.stat().st_size,
|
|
593
|
+
is_cached=False,
|
|
594
|
+
)
|
|
78
595
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
596
|
+
except Exception as e:
|
|
597
|
+
if isinstance(e, PluginUploadError):
|
|
598
|
+
raise
|
|
599
|
+
raise PluginUploadError(
|
|
600
|
+
f'Failed to upload file: {e}',
|
|
601
|
+
details={'target': target_path},
|
|
602
|
+
) from e
|
|
83
603
|
|
|
84
604
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
605
|
+
__all__ = [
|
|
606
|
+
# Enums
|
|
607
|
+
'PackageManager',
|
|
608
|
+
'UploadStage',
|
|
609
|
+
# Dataclasses
|
|
610
|
+
'UploadResult',
|
|
611
|
+
'BuildConfig',
|
|
612
|
+
# Type aliases
|
|
613
|
+
'UploadProgressCallback',
|
|
614
|
+
# Functions
|
|
615
|
+
'archive_plugin',
|
|
616
|
+
'archive_and_upload',
|
|
617
|
+
'build_and_upload',
|
|
618
|
+
'download_and_upload',
|
|
619
|
+
'modify_wheel_build_tag',
|
|
620
|
+
]
|